-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 106 KB
/
content.json
1
{"meta":{"title":"Zhuanghua","subtitle":"认清自己","description":"一只USTC菜鸟","author":"Zhuanghua","url":"http://yoursite.com","root":"/"},"pages":[{"title":"categories","date":"2019-05-26T15:07:00.000Z","updated":"2019-05-26T15:07:33.201Z","comments":true,"path":"categories/index.html","permalink":"http://yoursite.com/categories/index.html","excerpt":"","text":""},{"title":"tags","date":"2019-05-26T15:05:51.000Z","updated":"2019-05-26T15:06:14.801Z","comments":true,"path":"tags/index.html","permalink":"http://yoursite.com/tags/index.html","excerpt":"","text":""},{"title":"about","date":"2019-05-26T15:02:27.000Z","updated":"2019-05-26T15:32:20.430Z","comments":true,"path":"about/index.html","permalink":"http://yoursite.com/about/index.html","excerpt":"","text":"关于我男,USTC 固体地球物理学硕士在读,典型菜鸟。"}],"posts":[{"title":"Python 对象名称中的下划线","slug":"Python-对象名称中的下划线","date":"2019-08-17T07:33:29.000Z","updated":"2019-08-17T07:46:28.691Z","comments":true,"path":"2019/08/17/Python-对象名称中的下划线/","link":"","permalink":"http://yoursite.com/2019/08/17/Python-对象名称中的下划线/","excerpt":"单下划线 那种仅限从一个对象内部访问的“私有”实例变量在 Python 中并不存在。 但是,大多数 Python 代码都遵循这样一个约定:带有一个下划线的名称 (例如 _spam) 应该被当作是 API 的非仅供部分。","text":"单下划线 那种仅限从一个对象内部访问的“私有”实例变量在 Python 中并不存在。 但是,大多数 Python 代码都遵循这样一个约定:带有一个下划线的名称 (例如 _spam) 应该被当作是 API 的非仅供部分。 在 Python 中,单下划线用来对命名对象进行私有化,但只是一种约定,实际上是可以在外部调用的,例如:123456class Example(): def __init__(self): self._info = \"Life is short ...\" # 单下划线text = Example()print(text._info) 1Life is short ... 双下划线 任何形式为 __spam 的标识符(至少带有两个前缀下划线,至多一个后缀下划线)的文本将被替换为 _classname__spam,其中 classname 为去除了前缀下划线的当前类名称。 这个就不像前面那个单下划线命名的 _spam 对象了,这里的双下划线就有了实际作用,解释器会将双下划线命名的对象 __spam 加上其类的名称,以区别和其他类同名的命名,即 _classname__spam,并且不能被外部访问,例如:123456class Example(): def __init__(self): self.__info = \"Life is short ...\" # 双下划线text = Example()print(text.__info) 1234Traceback (most recent call last): File \"Untitled.py\", line 6, in <module> print(text.__info)AttributeError: 'Example' object has no attribute '__info' 不过使用 text._Example__info 还是可以访问滴!这种加上类名的改写规则的设计主要是为了避免意外冲突;访问或修改被视为私有的变量仍然是可能的。这在特殊情况下甚至会很有用,例如在调试器中。 魔法函数例如示例当中的 __init__ 也带下划线,这表示是 Python 内部的名字,用来区别其他用户自定义的命名,防止冲突。 参考9.6. 私有变量","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"下划线","slug":"下划线","permalink":"http://yoursite.com/tags/下划线/"}]},{"title":"TCP 的三次握手和四次挥手","slug":"TCP-的三次握手和四次挥手","date":"2019-08-17T03:44:48.000Z","updated":"2019-08-17T04:10:25.416Z","comments":true,"path":"2019/08/17/TCP-的三次握手和四次挥手/","link":"","permalink":"http://yoursite.com/2019/08/17/TCP-的三次握手和四次挥手/","excerpt":"TCP 创建过程和链接折除过程是由 TCP/IP 协议栈自动创建的,对于理解 TCP 底层运作机制,相当有帮助。","text":"TCP 创建过程和链接折除过程是由 TCP/IP 协议栈自动创建的,对于理解 TCP 底层运作机制,相当有帮助。 TCP 报文格式TCP/IP 协议的详细信息参看《TCP/IP协议详解》三卷本。下面是 TCP 报文格式图: 上图中几个字段的介绍: 序号:Seq 序号,占 32 位,用来标识从 TCP 源端向目的端发送的字节流,发起方发送数据时对此进行标记。 确认序号:Ack 序号,占 32 位,只有 ACK 标志位为 1 时,确认序号字段才有效,Ack=Seq+1。 标志位:共6个,即 URG、ACK、PSH、RST、SYN、FIN 等,具体含义如下: URG:紧急指针(urgent pointer)有效。 ACK:确认序号有效。 PSH:接收方应该尽快将这个报文交给应用层。 RST:重置连接。 SYN:发起一个新连接。 FIN:释放一个连接。 注意: 不要将确认序号 ack 与标志位中的 ACK 搞混了。 确认方 ack = 发起方 seq+1,两端配对。 TCP 三次握手所谓三次握手(Three-way Handshake),是指建立一个 TCP 连接时,需要客户端和服务器总共发送 3 个包。三次握手的目的是连接服务器指定端口,建立 TCP 连接,并同步连接双方的序列号和确认号并交换 TCP 窗口大小信息。客户端执行 connect()时,将触发三次握手: 第一次握手: 客户端发送一个 TCP 的 SYN 标志位 为 1,ACK 标志位为 0,seq 序号为 X ,ack 为 0 的包到服务器端。这个过程消耗一个序号。 第二次握手: 若服务器统一客户端的请求,则会发回确认包应答:即 SYN 标志位和 ACK 标志位均为 1,同时将序号设为 seq = Y,将确认序列号设为 ack = X + 1。seq 取 Y 和之前的过程没有关系,ack = X + 1是因为客户端发送的数据 seq = X,并且在发送的过程中消耗了一个序号,所以下一次应该收到的数据的地址为 X + 1,加的 1 就是消耗的一个序号: 第三次握手: 客户端需要确认,SYN = 0, ACK = 1, seq = X + 1, ack = Y + 1。seq 表示发送的数据序号为 X + 1。 整个过程如下: SYN 攻击 在三次握手过程中,服务器发送 SYN-ACK 之后,收到客户端的 ACK 之前的 TCP 连接称为半连接(half-open connect)。此时服务器处于 Syn_RECV 状态。当收到 ACK 后,服务器转入 ESTABLISHED 状态。 Syn攻击就是攻击客户端在短时间内伪造大量不存在的 IP 地址,向服务器不断地发送 syn 包,服务器回复确认包,并等待客户的确认,由于源地址是不存在的,服务器需要不断的重发直至超时,这些伪造的 SYN 包将长时间占用未连接队列,正常的 SYN 请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。Syn 攻击是一个典型的 DDOS 攻击。检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源 IP 地址是随机的,基本上可以断定这是一次 SYN攻击。在 Linux 下可以如下命令检测是否被 Syn 攻击 netstat -n -p TCP | grep SYN_RECV。一般较新的 TCP/IP 协议栈都对这一过程进行修正来防范 Syn 攻击,修改 tcp 协议实现。主要方法有 SynAttackProtect 保护机制、SYN cookies 技术、增加最大半连接和缩短超时时间等。但是不能完全防范 syn 攻击。 TCP 的四次挥手TCP 的连接的拆除需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,任何一方执行close() 操作即可产生挥手操作。 为什么建立连接是三次握手,而关闭连接却是四次挥手呢?这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。 实例演示 发送的第一个数据包: 在第一个包的时候SYN = 1,ACK = 1,seq的值为一个随机的值,ack为0。 发送的第二个数据包: 在第二个数据包的时候ACK = 1,SYN = 1,seq的值为一个随机数,ack的值为上一个数据包的seq的值+1。 发送的第三个数据包: 在第三个数据包中:SYN = 0,ACK = 1,seq的值为第一个数据包的seq的值+1,ack的值为第二个数据包的seq值+1。 总结: 在三次握手的时候,有四个字段是非常重要的。SYN 和 ACK均占一位,表示同步和确认。seq 和 ack 均为 32 位,分别为发送的数据的序号,和希望对方发送的数据的序号。 在三次握手的过程中,第一次和第二次的过程中 SYN 的值均为 1,并且每个过程都消耗掉一个序号,消耗一个序号的意思是:使得数据的序号加 1,好像是发送了一位数据,但是并没有真正的传递数据。 参考一次经典的tcp三次握手TCP的三次握手四次挥手","categories":[],"tags":[{"name":"TCP","slug":"TCP","permalink":"http://yoursite.com/tags/TCP/"},{"name":"三次握手","slug":"三次握手","permalink":"http://yoursite.com/tags/三次握手/"},{"name":"四次挥手","slug":"四次挥手","permalink":"http://yoursite.com/tags/四次挥手/"}]},{"title":"怎么才能写出无 Bug 的代码","slug":"怎么才能写出无-Bug-的代码","date":"2019-08-15T14:43:23.000Z","updated":"2019-08-15T15:05:27.994Z","comments":true,"path":"2019/08/15/怎么才能写出无-Bug-的代码/","link":"","permalink":"http://yoursite.com/2019/08/15/怎么才能写出无-Bug-的代码/","excerpt":"想必每一个码农都有一个追求,那就是很轻松地写出没有 Bug 的代码。","text":"想必每一个码农都有一个追求,那就是很轻松地写出没有 Bug 的代码。 我们总是相信一万小时定律,但你可曾想过这个世界上是有一些捷径的!不知你是否梦到过一位白发老者,他惊讶的対你说:少年,我看你骨骼精奇,是万中无一的武学奇才,维护世界和平就靠你了。。。 哎,同学,快醒醒! 从现在起,你不必骨骼惊奇,不必是天才,不必要去完成一万小时定律,只需三分钟,3w+ star,2k+ fork github 项目打通你的任督二脉,瞬间写出无 Bug 代码,从此走向人生巅峰!","categories":[],"tags":[{"name":"nocode","slug":"nocode","permalink":"http://yoursite.com/tags/nocode/"}]},{"title":"Python常用魔法函数","slug":"Python常用魔法函数","date":"2019-08-14T13:58:41.000Z","updated":"2019-08-14T14:14:04.894Z","comments":true,"path":"2019/08/14/Python常用魔法函数/","link":"","permalink":"http://yoursite.com/2019/08/14/Python常用魔法函数/","excerpt":"这篇博客整理了一下常用的 Python 魔法函数","text":"这篇博客整理了一下常用的 Python 魔法函数 __init__()所有类的超类 object,有一个默认包含 pass 的 __init__() 实现,这个函数会在对象初始化的时候调用,我们可以选择实现,也可以选择不实现,一般建议是实现的,不实现对象属性就不会被初始化,虽然我们仍然可以对其进行赋值,但是它已经成了隐式的了,编程时显示远比隐式的更好。 __new__()在object类中存在一个静态的 __new__(cls, *args, **kwargs) 方法,该方法需要传递一个参数 cls,cls 表示需要实例化的类,此参数在实例化时由 Python 解释器自动提供, __new__ 方法必须有返回值,且返回的是被实例化的实例,只有在该实例返回后才会调用 __init__ 来进行初始化,初始化所用的实例就是 __new__ 返回的结果,也就可以认为是 self。 __iter__()只要定义了 __iter__() 方法对象,就是可迭代对象;这意味着,我们可以迭代我们自己定义的对象。 __next__()函数next() 是 iterator 区别于 iterable 的关键,它允许我们显式地获取一个元素。当调用 next() 方法时,实际上产生了 2 个操作:一是更新 iterator 状态,令其指向后一项,以便下一次调用,二是返回当前结果。 __call__()对象通过提供 __call__() 方法可以模拟函数的行为,如果一个对象提供了该方法,就可以像函数一样使用它。 __len__()len 调用后会调用对象的 __len__ 函数,我们可以为其定制输出。 __str__()直接打印对象的实现方法,__str__ 是被 print 函数调用的,一般都是 return 一个什么东西,这个东西应该是以字符串的形式表现的。如果不是要用 str() 函数转换,我们可以直接 print 的对象都是实现了 __str__ 这个方法的,比如 dict。 __repr__()函数 str() 用于将值转化为适于人阅读的形式,而 repr() 转化为供解释器读取的形式,某对象如果没有适于人阅读的解释形式的话,我们可以定制 __repr__ 的输出。 __setitem__()该函数可以给对象赋值,我们可以以下标的方式对其进行操作 __getitem__()与上函数相反,__getitem__ 可以使对象支持以下标的方式获取值。 __delitem__()该函数支持以下标方式删除对象数据,实现了这三个函数,这个类就像字典一样,具备了基本的增删查功能,有时候这样写会很方便。 __del__()这可以说是一个析构器,或者回收器,在对象引用数降到0时执行,有时可能还需要等一会再执行,所以一般不推荐使用,但是在代码中我们偶尔可以用它来实现一些必须要做的,但是并不紧急的事。 __setattr__()该函数可以设置函数的属性。 __getattr__()获取对象属性,只有在属性没有找到的时候调用。 __getattribute__()该函数和上面介绍的 __getattr__ 很像,都是获取属性,但是 __getattr__ 是在属性不存在时被调用,而 __getattribute__ 是无条件被调用,这样会方便我们做一些控制,需要注意,一旦定义 了 __getattribute__,则 __getattr__ 不再会被调用,除非显式调用。 __delattr__()本函数的作用是删除属性,实现了该函数的类可以用 del 命令来删除属性。 持续更新中…","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"魔法函数","slug":"魔法函数","permalink":"http://yoursite.com/tags/魔法函数/"}]},{"title":"Python中进制转换与位操作","slug":"Python中进制转换与位操作","date":"2019-08-14T05:48:33.000Z","updated":"2019-08-14T11:35:23.678Z","comments":true,"path":"2019/08/14/Python中进制转换与位操作/","link":"","permalink":"http://yoursite.com/2019/08/14/Python中进制转换与位操作/","excerpt":"Python中的进制表示计算机中的数都是使用二进制表示的,Python中数字加前缀$0b$,表示是二进制,类似的八进制和十六进制的前缀分别为 $0o$ 和 $0x$。","text":"Python中的进制表示计算机中的数都是使用二进制表示的,Python中数字加前缀$0b$,表示是二进制,类似的八进制和十六进制的前缀分别为 $0o$ 和 $0x$。 12345670b1 # 二进制表示的10b10 # 二进制表示的20b1111 # 二进制表示的150o10 # 八进制表示的80o17 # 八进制表示的150x10 # 十六进制表示的160xff # 十六进制表示的255 Python中的进制转换 Python中的进制转换函数 int():其他进制转十进制 bin():十进制转二进制 oct():十进制转八进制 hex():十进制转十六进制 其他进制转十进制 123int('011', 2) # 二进制转十进制(3)int('050', 8) # 八进制转十进制(40)int('0x4', 16) # 十六进制转十进制(4) 上述一定不要忘记加引号,因为二进制、八进制、十六进制都是一个字符串 十进制转其他进制 123456bin(int('3',10)) # 十进制转二进制('0b11')bin(3) # 十进制转二进制('0b11')oct(int('3',10)) # 十进制转二进制('0o3')oct(3) # 十进制转八进制('0o3')hex(int('3',10)) # 十进制转二进制('0x3')hex(3) # 十进制转十六进制('0x3') Python中的位操作Python中共有一下几种位操作:123456x >> y # 返回 x 向右移 y 位得到的结果x << y # 返回 x 向左移 y 位得到的结果x & y # 且操作,返回结果的每一位是 x 和 y 中对应位做 and 运算的结果,只有 1 and 1 = 1,其他情况位0x | y # 或操作,返回结果的每一位是 x 和 y 中对应位做 or 运算的结果,只有 0 or 0 = 0,其他情况位1~x # 反转操作,对 x 求的每一位求补,只需记住结果是 -x - 1x ^ y # 或非运算,如果 y 对应位是0,那么结果位取 x 的对应位,如果 y 对应位是1,取 x 对应位的补,当y位数不够时,从高位补零。 左移和右移操作 顾名思义,左移和右移的意思就是把位数整体向左或者向右移动若干位。比如1111向右移一位就变成了0111,原来没有的位自动填0,超出范围的位舍弃掉。12340b1111 >> 1 == 0b111 == 70b1010 << 2 == 0b101000 == 400b111111 >> 3 == 0b111 == 70b1 << 4 == 0b10000 == 16 向右移1位可以看成除以2,向左移一位可以看成乘以2。移动n位可以看成乘以或者除以2的n次方。128 >> 2 == 8 / 2 / 2 == 0b1000 >> 2 == 0b10 == 28 << 2 == 8 * 2 * 2 == 0b1000 << 2 == 0b100000 == 32 且操作 对于单个位的且操:121 & 1 == 11 & 0 == 0 & 1 == 0 & 0 == 0 两个数字的且操作就是对每一位进行且操作取结果:1230b1 & 0b0 == 00b1111 & 0b1010 == 0b1010 == 100b1010 & 0b1100 == 0b1000 == 8 或操作 对于单个位的或操作:121 | 0 == 0 | 1 == 1 | 1 == 10 | 0 == 0 两个数字的或操作就是对每一位进行或操作取结果:1230b1 | 0b0 == 0b1 ==10b1000 | 0b0111 == 0b1111 == 150b1010 | 0b1100 == 0b1110 == 14 或非操作 对于 x ^ y,如果y的位是0,那么取x的原始值,如果y的位是1,那么取x此位的补:120b1111 ^ 0b0101 = 0b10100b1111 ^ 0b1 = 0b1110 # 自动填0 反转操作 python的反转操作只接受一个参数n,n必须是整数,效果是对n的内部表示的每一位求补,运算结果位$-n-1$: ~8 = -9 位操作的应用持续更新中…","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"进制转换","slug":"进制转换","permalink":"http://yoursite.com/tags/进制转换/"},{"name":"位操作","slug":"位操作","permalink":"http://yoursite.com/tags/位操作/"}]},{"title":"Python实现二叉树重建","slug":"Python实现二叉树重建","date":"2019-07-29T06:12:07.000Z","updated":"2019-07-29T06:20:43.921Z","comments":true,"path":"2019/07/29/Python实现二叉树重建/","link":"","permalink":"http://yoursite.com/2019/07/29/Python实现二叉树重建/","excerpt":"通常树有如下几种遍历方式: 前序遍历:先访问根结点,再访问左子结点,最后访问右子结点。 中序遍历:先访问左子结点,再访问根结点,最后访问右子结点。 后序遍历:先访问左子结点,再访问右子结点,最后访问根结点。 层序遍历:即广度优先遍历,先访问一级节点,后是二级节点…","text":"通常树有如下几种遍历方式: 前序遍历:先访问根结点,再访问左子结点,最后访问右子结点。 中序遍历:先访问左子结点,再访问根结点,最后访问右子结点。 后序遍历:先访问左子结点,再访问右子结点,最后访问根结点。 层序遍历:即广度优先遍历,先访问一级节点,后是二级节点… 最少需要两种遍历方式,才能重建二叉树。但前序和后序构建的二叉树不唯一,因为前序和后序在本质上都是将父节点与子结点进行分离,但并没有指明左子树和右子树的能力,因此得到这两个序列只能明确父子关系,而不能确定一个二叉树。总之,必须要有中序才能构建,因为没有中序,就没办法确定树的形状。 根据前序和中序遍历重建二叉树思路: 前序遍历序列中,第一个数字总是树的根结点的值。在中序遍历序列中,根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边。剩下的我们可以递归来实现,具体如图: 前序遍历顺序:root — preorder of left branch — preorder of right branch 中序遍历顺序:inorder of left branch — root — inorder of right branch 1234567891011121314151617181920class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = Noneclass Solution: # 返回构造的TreeNode根节点 def reConstructBinaryTree(self, pre, tin): # write code here if len(pre) == 0: return None root = TreeNode(pre[0]) if len(pre) == 1: return root pos = tin.index(pre[0]) root.left = self.reConstructBinaryTree(pre[1:pos+1], tin[:pos]) root.right = self.reConstructBinaryTree(pre[pos+1:], tin[pos+1:]) return root 根据中序遍历和后序遍历重建二叉树思路: 在中序遍历序列中,根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边。而在后序遍历序列中,根节点位于序列的最后。剩下的我们可以递归来实现。 中序遍历顺序:inorder of left branch — root — inorder of right branch 后序遍历顺序:postorder of left branch — postorder of right branch — root 1234567891011121314151617181920class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = Noneclass Solution: # 返回构造的TreeNode根节点 def reConstructBinaryTree(self, tin, post): # write code here if len(post) == 0: return None root = TreeNode(post[-1]) if len(post) == 1: return root pos = tin.index(post[-1]) root.left = self.reConstructBinaryTree(tin[:pos], post[:pos]) root.right = self.reConstructBinaryTree(tin[pos+1:], post[pos:-1]) return root 根据前序遍历和后序遍历重建二叉树因为前序和后序在本质上都是将父节点与子结点进行分离,但并没有指明左子树和右子树的能力,因此得到这两个序列只能明确父子关系,而不能确定一个二叉树, 如: 总之,必须要有中序才能构建,因为没有中序,你没办法确定树的形状。 思路: 前序遍历顺序:root — preorder of left branch — preorder of right branch 后序遍历顺序:postorder of left branch — postorder of right branch — root 假如说最终的二叉树可以被序列化的表述为 [1, 2, 3, 4, 5, 6, 7],那么其前序遍历为 [1] + [2, 4, 5] + [3, 6, 7],而后序遍历为 [4, 5, 2] + [6, 7, 3] + [1]。如果我们知道左分支有多少个结点,我们就可以对这些数组进行分组,并用递归生成树的每个分支。 算法: 我们令左分支有 L 个节点。我们知道 左分支的头节点 为 pre[1],但它也出现在 左分支的后序表示的最后。所以 pre[1] = post[L-1](因为结点的值具有唯一性),因此 L = post.indexOf(pre[1]) + 1。 现在在我们的递归步骤中,左分支由 pre[1 : L+1] 和 post[0 : L] 重新分支,而右分支将由 pre[L+1 : N] 和 post[L : N-1] 重新分支。 123456789101112131415161718class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None class Solution(object): def constructFromPrePost(self, pre, post): if not pre: return None root = TreeNode(pre[0]) if len(pre) == 1: return root L = 1 + post.index(pre[1]) root.left = self.constructFromPrePost(pre[1:L+1], post[:L]) root.right = self.constructFromPrePost(pre[L+1:], post[L:]) return root 二叉树重建总结一、根据前序、中序遍历重建二叉树 根据前序序列的第一个元素建立根结点 在中序序列中找到根结点位置,确定左右子树 递归由左子树的前序序列和中序序列建立左子树 递归由右子树的前序序列和中序序列建立右子树 二、根据中序、后序遍历重建二叉树 根据后序序列的最后一个元素建立根结点 在中序序列中找到根结点位置,确定左右子树 递归由左子树的后序序列和中序序列建立左子树 递归由右子树的后序序列和中序序列建立右子树 三、前序、后序遍历重建二叉树(不唯一) 根据前序序列的第一个元素建立根结点 令左分支有 L 个节点。根据前序序列,我们知道左分支的头节点 为 pre[1],但它也出现在后序序列的左分支的最后。所以 pre[1] = post[L-1](因为结点的值具有唯一性),因此 L = post.indexOf(pre[1]) + 1。 递归由左子树的前序序列和后序序列建立左子树 递归由右子树的前序序列和后序序列建立右子树 综上可知,二叉树的重建关键在于确定根节点和左右分支,一旦这些确定了,那么即可用递归重建此二叉树。","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"二叉树重建","slug":"二叉树重建","permalink":"http://yoursite.com/tags/二叉树重建/"}]},{"title":"Python实现二叉树及其遍历","slug":"Python实现二叉树及遍历","date":"2019-07-24T06:08:30.000Z","updated":"2019-07-24T12:02:43.291Z","comments":true,"path":"2019/07/24/Python实现二叉树及遍历/","link":"","permalink":"http://yoursite.com/2019/07/24/Python实现二叉树及遍历/","excerpt":"树结构多种多样,不过最常用的还是二叉树。顾名思义,二叉树每个节点最多有两个叉,也就是两个子节点,分别是左子节点和右子节点。","text":"树结构多种多样,不过最常用的还是二叉树。顾名思义,二叉树每个节点最多有两个叉,也就是两个子节点,分别是左子节点和右子节点。 树的组成及相关概念 树的组成 根节点 父节点 子节点 兄弟结点 叶子节点或叶节点 树的概念 节点的高度:节点到叶子节点最长路径(边数) 节点的深度:根节点到这个节点所经历的边的个数 节点的层数:节点的深度 + 1 树的高度:根节点的高度 树的构建12345678910111213141516171819202122232425262728293031323334353637class Node(): ''' 结点类 ''' def __init__(self, value=None, lft=None, rgt = None): self.value = value self.lft = lft self.rgt = rgt def __str__(self): return str(self.value)class Tree(): ''' 树类 ''' def __init__(self): self.root = Node() self.myqueue = [] # 用来存放子树的三个节点,依次为root, left, right def add(self, elem): ''' 构建树结构 ''' node = Node(elem) if self.root.value == None: # 树为空,进行赋值 self.root = node self.myqueue.append(self.root) else: # 此树不为空,但子节点还没补齐 treeNode = self.myqueue[0] if treeNode.lft == None: treeNode.lft = node self.myqueue.append(treeNode.lft) else: treeNode.rgt = node self.myqueue.append(treeNode.rgt) self.myqueue.pop(0) # 该节点如果存在右子树,将此结点丢弃 树的遍历 递归方式实现深度优先遍历 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566class Node(): ''' 结点类 ''' def __init__(self, value=None, lft=None, rgt = None): self.value = value self.lft = lft self.rgt = rgt def __str__(self): return str(self.value)class Tree(): ''' 树类 ''' def __init__(self): self.root = Node() self.myqueue = [] # 用来存放子树的三个节点,依次为root, left, right def add(self, elem): node = Node(elem) if self.root.value == None: # 树为空,进行赋值 self.root = node self.myqueue.append(self.root) else: # 此树不为空,但子节点还没补齐 treeNode = self.myqueue[0] if treeNode.lft == None: treeNode.lft = node self.myqueue.append(treeNode.lft) else: treeNode.rgt = node self.myqueue.append(treeNode.rgt) self.myqueue.pop(0) # 该节点如果存在右子树,将此结点丢弃 # 递归方式前中后序遍历 def preOrder(self, root): ''' 前序遍历 ''' if root == None: return else: print(root.value) self.preOrder(root.lft) self.preOrder(root.rgt) def inOrder(self, root): ''' 中序遍历 ''' if root == None: return self.inOrder(root.lft) print(root.value) self.inOrder(root.rgt) def postOrder(self, root): ''' 后序遍历 ''' if root == None: return self.postOrder(root.lft) self.postOrder(root.rgt) print(root.value) 利用栈实现非递归方式深度优先遍历 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091class Node(): ''' 结点类 ''' def __init__(self, value=None, lft=None, rgt = None): self.value = value self.lft = lft self.rgt = rgt def __str__(self): return str(self.value)class Tree(): ''' 树类 ''' def __init__(self): self.root = Node() self.myqueue = [] # 用来存放子树的三个节点,依次为root, left, right def add(self, elem): node = Node(elem) if self.root.value == None: # 树为空,进行赋值 self.root = node self.myqueue.append(self.root) else: # 此树不为空,但子节点还没补齐 treeNode = self.myqueue[0] if treeNode.lft == None: treeNode.lft = node self.myqueue.append(treeNode.lft) else: treeNode.rgt = node self.myqueue.append(treeNode.rgt) self.myqueue.pop(0) # 该节点如果存在右子树,将此结点丢弃 # 非递归方式前中后序遍历,利用栈实现 def preOrder_stack(self, root): ''' 利用栈实现前序遍历 ''' if root == None: return node = root mystack = [] # 保存遍历的节点 while node or mystack: while node: # 从根节点开始,先遍历左子树 print(node.value) # 先打印再入栈 mystack.append(node) node = node.lft node = mystack.pop() # 弹出栈顶元素,开始遍历右子树 node = node.rgt # 每弹出一个结点,紧接着就访问其右子树 def inOrder_stack(self, root): ''' 利用栈实现中序遍历 ''' if root == None: return node = root mystack = [] while node or mystack: while node: # 从根节点开始,一直找到左子树到最后一个节点 mystack.append(node) # 先入栈后打印 node = node.lft node = mystack.pop() # pop顺序: left, root, right print(node.value) node = node.rgt def postOrder_stack(self, root): ''' 利用栈实现后序遍历 ''' if root == None: return node = root mystack1 = [] # 存放正在访问的节点 mystack2 = [] # 存放后序遍历的逆序 mystack1.append(node) while mystack1: node = mystack1.pop() if node.lft: mystack1.append(node.lft) if node.rgt: mystack1.append(node.rgt) mystack2.append(node) while mystack2: print(mystack2.pop().value) 广度优先遍历 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051class Node(): ''' 结点类 ''' def __init__(self, value=None, lft=None, rgt = None): self.value = value self.lft = lft self.rgt = rgt def __str__(self): return str(self.value)class Tree(): ''' 树类 ''' def __init__(self): self.root = Node() self.myqueue = [] # 用来存放子树的三个节点,依次为root, left, right def add(self, elem): node = Node(elem) if self.root.value == None: # 树为空,进行赋值 self.root = node self.myqueue.append(self.root) else: # 此树不为空,但子节点还没补齐 treeNode = self.myqueue[0] if treeNode.lft == None: treeNode.lft = node self.myqueue.append(treeNode.lft) else: treeNode.rgt = node self.myqueue.append(treeNode.rgt) self.myqueue.pop(0) # 该节点如果存在右子树,将此结点丢弃 def levelOrder(self, root): ''' 层次遍历 ''' if root == None: return myqueue = [] node = root myqueue.append(node) while myqueue: node = myqueue.pop(0) print(node.value) if node.lft != None: myqueue.append(node.lft) if node.rgt != None: myqueue.append(node.rgt) 总结: 二叉树的遍历可以分为两种,即广度优先遍历和深度优先遍历。 广度优先遍历如层次遍历,按照一度优先、二度优先…的顺序进行逐层遍历。 深度优先遍历如前序、中序、后序遍历。 广度优先一般使用队列来实现,而深度优先则使用递归或栈来实现,因为大部分情况下栈可以实现递归。","categories":[],"tags":[{"name":"Binary Tree","slug":"Binary-Tree","permalink":"http://yoursite.com/tags/Binary-Tree/"},{"name":"二叉树","slug":"二叉树","permalink":"http://yoursite.com/tags/二叉树/"},{"name":"遍历","slug":"遍历","permalink":"http://yoursite.com/tags/遍历/"},{"name":"递归","slug":"递归","permalink":"http://yoursite.com/tags/递归/"},{"name":"非递归","slug":"非递归","permalink":"http://yoursite.com/tags/非递归/"},{"name":"广度优先","slug":"广度优先","permalink":"http://yoursite.com/tags/广度优先/"},{"name":"深度优先","slug":"深度优先","permalink":"http://yoursite.com/tags/深度优先/"},{"name":"队列","slug":"队列","permalink":"http://yoursite.com/tags/队列/"},{"name":"栈","slug":"栈","permalink":"http://yoursite.com/tags/栈/"}]},{"title":"Mac 终端 PS1 变量配置","slug":"Mac-终端-PS1-变量配置","date":"2019-07-22T13:27:51.000Z","updated":"2019-07-23T03:56:55.612Z","comments":true,"path":"2019/07/22/Mac-终端-PS1-变量配置/","link":"","permalink":"http://yoursite.com/2019/07/22/Mac-终端-PS1-变量配置/","excerpt":"默认的终端提示符像这样zhangsan@zhangsandeMacBook-Pro ~$,又长又俗,丑到爆,所以我们有必要配置一下终端的命令提示符,顿时就会感觉清爽了很多! 先来看一下效果图","text":"默认的终端提示符像这样zhangsan@zhangsandeMacBook-Pro ~$,又长又俗,丑到爆,所以我们有必要配置一下终端的命令提示符,顿时就会感觉清爽了很多! 先来看一下效果图 配置如下12345678910## colorful terminal setting# set promptexport CLICOLOR=1#export PS1='[\\u@\\h] \\W\\$ '#symbol to select: ☀ ❄ ✎ ♪ ★ ✈ export PS1='\\[\\e]2;\\u@\\h:\\w\\a\\]\\[\\033[0;36m\\]♪ \\u\\[\\033[01;31m\\] \\W \\$\\[\\033[00m\\] '# set colorful filesexport LS_OPTIONS='--color=auto'export CLICOLOR='Yes'export LSCOLORS='cxfxcxdxbxegedabagGxGx' 参数解释 转义字符 12345678910111213141516171819\\u 用户名\\h 主机名第一部分\\H 主机名全称\\w 当前工作目录(如 “/home/username/mywork”)\\W 当前工作目录的“基名 (basename)”(如 “mywork”)\\t 24 小时制时间\\T 12 小时制时间\\@ 带有 am/pm 的 12 小时制时间\\d “Sat Dec 18″ 格式的日期\\s shell 的名称(如 “bash”)\\v bash 的版本(如 2.04)\\V Bash 版本\\n 换行符\\r 回车符\\\\ 反斜杠\\a ASCII 响铃字符(同 07)\\e ASCII 转义字符(同33)\\[ 这个序列应该出现在不移动光标的字符序列(如颜色转义序列)之前。它使 bash 能够正确计算自动换行。\\] 这个序列应该出现在非打印字符序列之后。 颜色设置 123456789前景 背景 颜色30 40 黑色31 41 红色32 42 绿色33 43 黄色34 44 蓝色35 45 紫红色36 46 青蓝色37 47 白色","categories":[],"tags":[{"name":"Mac","slug":"Mac","permalink":"http://yoursite.com/tags/Mac/"},{"name":"终端","slug":"终端","permalink":"http://yoursite.com/tags/终端/"},{"name":"PS1变量","slug":"PS1变量","permalink":"http://yoursite.com/tags/PS1变量/"}]},{"title":"Python散列表","slug":"Python散列表","date":"2019-07-16T10:00:00.000Z","updated":"2019-07-16T10:01:48.574Z","comments":true,"path":"2019/07/16/Python散列表/","link":"","permalink":"http://yoursite.com/2019/07/16/Python散列表/","excerpt":"散列表 散列表是一种数据的集合,其中的每个数据都通过某种特定的方式进行存储以方面日后的查找。基于它的搜索算法的时间复杂度为O(1)。散列表的每一个位置叫做槽,能够存放一个数据项,并以从0开始递增的整数命名。例如, 第一个槽记为0,下一个记为1,再下一个记为2,并以此类推。在初始条件下,散列表中是没有任何数据的,即每个槽都是空的。某个数据项与在散列表中存储它的槽之间的映射叫做散列函数。一般地,我们把槽被占据的比例叫做负载因子,两个甚至多个数据需要存储在同一个槽中,这种情况被称为冲突。","text":"散列表 散列表是一种数据的集合,其中的每个数据都通过某种特定的方式进行存储以方面日后的查找。基于它的搜索算法的时间复杂度为O(1)。散列表的每一个位置叫做槽,能够存放一个数据项,并以从0开始递增的整数命名。例如, 第一个槽记为0,下一个记为1,再下一个记为2,并以此类推。在初始条件下,散列表中是没有任何数据的,即每个槽都是空的。某个数据项与在散列表中存储它的槽之间的映射叫做散列函数。一般地,我们把槽被占据的比例叫做负载因子,两个甚至多个数据需要存储在同一个槽中,这种情况被称为冲突。 散列函数对于一组给定的数据项,如果一个散列函数可以将每一个数据项都映射到不同的槽中,那么这样的散列函数叫做完美散列函数。一种实现完美散列函数的方法就是扩大散列表的尺寸,直到所有可能的数据项变化范围都被散列表所包含。这一方法保证了每一个数据项都有一个不同的槽。尽管这个方法对于少量数据是可行的,但是当它面对大量可能的数据项时显得捉襟见肘。我们的目标是创建一个能够将冲突的数量降到最小,计算方便,并且最终将数据项分配到散 列表中的散列函数。有几种普通的方法去扩展求余方法: 折叠法:首先将数据分成相同长度的片段(最后一个片段长度可能不等)。接着将这些片段相加,再求余得到其散列值。 平方取中法:我们首先将数据取平方,然后取平方数的某一部分,然后再进行求余运算。我们也可以为非数字的数据项,例如字符串创建散列表,’cat’可以看做一个连续的ASCII数值。123456# 用ASCII数值散列一个字符串def hash(astring, tablesize): sum = 0 for i in range(len(astring)): sum = sum + ord(astring[i]) return sum%tablesize 当我们用散列函数纪录散列值的时候,颠倒的字母构成的单词会得到相同的散列值。为了纠正这一情况,我们可以将字母的位置作为权重因子。123456# 用ASCII数值*权重散列一个字符串def hash(astring, tablesize): sum = 0 for i in range(len(astring)): sum = sum + ord(astring[i])*(i+1) return sum%tablesize 冲突当两个数据散列到相同的槽,我们必须用一种系统化的方法将第二个数据放到散列表里。这个过程叫做冲突解决。 一种解决冲突的方法就是搜索散列表并寻找另一个空的槽来存放这个有冲突的数据。一种简单的方法就是从发生冲突的位置开始顺序向下开始寻找,直到我们遇到第一个空的槽。注意到我们可能需要回到第一个槽(循环)来实现覆盖整个散列表。这种冲突解决方法叫做开放地址,它试图在散列表中去寻找下一个空的槽。通过系统地向后搜索每一个槽,我们将这种实现开放地址的技术叫做线性探测。 线性探测法的一个缺点是产生集中的趋势:数据会在表中聚集。这意味着如果对于同一散列值产生了许多冲突时,周边的一系列槽都将会被线性探测填充。这将会对正在被填入的新数据产生影响。一种解决集中问题的方法是扩展线性探测技术,我们不再按顺序一个一个地寻找新槽,而是跳过一些槽,这样能更加平均地分配出现冲突的数据,进而潜在地减少集中问题出现的次数。下图展示了同样的数据项如何通过“+3”线性探测的方法解决冲突的。“+3”表示一旦一个冲突出现,我们将每次跳过两个槽来寻找下一个新的空槽。 另一种线性探测方法叫做二次探测法。我们不是每次在冲突中选择跳过固定个数的槽,而是使用一个再散列函数使每次跳过槽的数量会依次增加1,3,5,7,9,以此类推。这意味着如果原槽为第h个,那么再散列时访问的槽为第h+1,h+4,h+9,h+16个,以此类推。换言之,二次探测法使用一个连续的完全平方数数列作为它的跳跃值。图11显示了我们的例子在运用二次探测法时的 填充结果。 另一个解决冲突的替代方法是允许每一个槽都能填充一串而不是一个数据(称作链)。链能允许多个数据填在散列表中的同一个位置上。当冲突发生时,数据还是填在本应该位于的槽中。 随着一个槽中填入的数据的增多,搜索的难度也就随之增加。下图显示了数据在用数据链方法填入散列表的结果。 实现映射的抽象数据类型 字典是Python中最有用的数据类型之一。字典是一个可以储存键-值对的关联数据类型。键是用来查找和它相关联的值的。通常把这个想法称作映射。映射的抽象数据类型定义如下:它以一个键与一个值之间关联的无序集合作为结构。映射中的键都是独特的,以保证和值之间的一一对应关系。 映射的相关操作: Map():产生一个新的空映射 put():往映射中添加键-值对。如果密钥已经存在,那么将旧的数据值置换为新的。 get(key):给定一个 key 值,返回关联的数据,若不存在,返回None。 del:从映射中删除一个密钥-数据值对,声明形式为del map[key]。 len():返回映射中的存储密钥-数据值对的个数。 in:表述为key in map,如果给定的键在map中返回True,否则返回False。 python实现映射12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758# 创建哈希表类class Hashtable(): def __init__(self): self.size = 11 # 定义两个列表分别存储键和值 self.slots = [None]*self.size self.data = [None]*self.size # 定义put()方法 def put(self, key, data): hashvalue = self.hashFunc(key, len(self.slots)) # hashvalue为计算出的一个整数,此处相当于列表的下标 if self.slots[hashvalue] == None: # 创建新的键值对 self.slots[hashvalue] = key self.data[hashvalue] = data else: if self.slots[hashvalue] == key: # 该键的位置没有被rehash过,重新赋予键新值 print('***', hashvalue, self.slots[hashvalue], key) self.data[hashvalue] = data else: # 该键的位置被rehash过,重新赋予键新值 nextslot = self.rehash(hashvalue, len(self.slots)) while self.slots[nextslot] != None and self.slots[nextslot] != key: nextslot = self.rehash(nextslot, len(self.slots)) if self.slots[nextslot] == None: self.slots[nextslot] = key self.data[nextslot] = data else: self.data[nextslot] = data # 定义hashFunc()方法 def hashFunc(self, key, size): return key%size # 定义rehash()方法 def rehash(self, oldvalue, size): return (oldvalue + 1)%size # 定义get()方法 def get(self, key): initslot = self.hashFunc(key, len(self.slots)) data = None stop = False found = False pos = initslot while self.slots[pos] != None and not found and not stop: if self.slots[pos] == key: found = True data = self.data[pos] else: pos = self.rehash(pos, len(self.slots)) if pos == initslot: # 循环到开始处,表示没有搜索到 stop = True return data def __getitem__(self, key): return self.get(key) def __setitem__(self, key, data): self.put(key, data)","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"Hashtable","slug":"Hashtable","permalink":"http://yoursite.com/tags/Hashtable/"},{"name":"散列表","slug":"散列表","permalink":"http://yoursite.com/tags/散列表/"},{"name":"散列函数","slug":"散列函数","permalink":"http://yoursite.com/tags/散列函数/"},{"name":"冲突","slug":"冲突","permalink":"http://yoursite.com/tags/冲突/"},{"name":"映射","slug":"映射","permalink":"http://yoursite.com/tags/映射/"}]},{"title":"Python搜索","slug":"Python搜索","date":"2019-07-14T09:58:33.000Z","updated":"2019-07-16T10:06:41.627Z","comments":true,"path":"2019/07/14/Python搜索/","link":"","permalink":"http://yoursite.com/2019/07/14/Python搜索/","excerpt":"搜索 在Python中,有一个非常简单的方法来判别特定项是否在列表中。我们使用in这个运算符。但为了说明这些搜索算法的工作原理以及它们相互比较时的优劣之分,我们还是自己实现这些算法。","text":"搜索 在Python中,有一个非常简单的方法来判别特定项是否在列表中。我们使用in这个运算符。但为了说明这些搜索算法的工作原理以及它们相互比较时的优劣之分,我们还是自己实现这些算法。 无序列表的顺序搜索123456789def sequentialSearch(alist, item): pos = 0 found = False while pos < len(alist) and not found: if alist[pos] == item: found = True else: pos += 1 return found 有序列表的顺序搜索123456789101112def orderedSequentialSearch(alist, item): pos = 0 found = False stop = False while pos < len(alist) and not found and not stop: if alist[pos] == item: found = True elif alist[pos] > item: stop = True else: pos += 1 return found 二分法搜索1234567891011121314# 二分法搜索返回True或Falsedef binarySearch(alist, item): head = 0 tail = len(alist) - 1 found = False while head <= tail and not found: mid = (head + tail)//2 if alist[mid] == item: found = True elif alist[mid] < item: head = mid + 1 else: tail = mid - 1 return found 12345678910111213# 二分法搜索返回indexdef binarySearch2(alist, item): head = 0 tail = len(alist) - 1 while head <= tail: mid = (head + tail)//2 if alist[mid] == item: return mid elif alist[mid] < item: head = mid + 1 else: tail = mid - 1 return \"The item '{}' is not found!\".format(item) 123456789101112# 二分法递归搜索,返回True或Falsedef binarySearch3(alist, item): if len(alist) == 0: return \"The item '{}' is not found!\".format(item) else: mid = len(alist)//2 if alist[mid] == item: return True elif alist[mid] < item: return binarySearch3(alist[mid+1:], item) else: return binarySearch3(alist[:mid], item) 1234567891011121314151617# 二分法递归搜索,返回indexdef binarySearch3(alist, item, head, tail): ''' 因为返回值是index,所以每次递归传回的列表必须是原列表, 这样才能保证index是在原列表中的index,而不是每次递归 缩小的列表的index ''' if head > tail: return \"The item '{}' is not found!\".format(item) else: mid = (head + tail)//2 if alist[mid] == item: return True elif alist[mid] < item: return binarySearch3(alist, item, mid+1, tail) else: return binarySearch3(alist, item, head, mid-1)","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"Search","slug":"Search","permalink":"http://yoursite.com/tags/Search/"},{"name":"Binary Search","slug":"Binary-Search","permalink":"http://yoursite.com/tags/Binary-Search/"}]},{"title":"Python递归","slug":"Python递归","date":"2019-07-13T09:55:51.000Z","updated":"2019-07-16T10:06:25.394Z","comments":true,"path":"2019/07/13/Python递归/","link":"","permalink":"http://yoursite.com/2019/07/13/Python递归/","excerpt":"递归 递归是一种解决问题的方法,它把一个问题分解为越来越小的子问题,直到问题的规模小到 可以被很简单直接解决。而递归函数就是一种调用自身的函数。","text":"递归 递归是一种解决问题的方法,它把一个问题分解为越来越小的子问题,直到问题的规模小到 可以被很简单直接解决。而递归函数就是一种调用自身的函数。 递归求和1234567891011# 列表递归求和方法一def listSum(lst): if len(lst) == 1: return lst[0] else: return lst[0] + listSum(lst[1:])# 列表递归求和方法二def iterSum(arr): head, *tails = arr return head + iterSum(tails) if tails else head 递归三大定律 递归算法必须要递归地调用自己 递归算法必须有一个基本结束条件 递归算法必须改变自己的状态并向基本结束条件靠近1234567# 递归转换整数到字符串def to_str(n, str): convert_str = '0123456789ABCDEF' if n < base: return convert_str[n] else: return to_str(n//base, base) + convert_str[n%base] 123456# 写一个函数,接受一个字符串作为参数,并返回一个反向的新字符串。def reverse(s): if len(s) == 1: return s else: return reverse(s[1:]) + s[0] 总结: 递归的逻辑就是利用优美的程序把问题分解为更小更简单的子问题来解决。 谢尔宾斯基三角形汉诺塔问题动态规划","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"Recursion","slug":"Recursion","permalink":"http://yoursite.com/tags/Recursion/"}]},{"title":"Python实现有序列表","slug":"Python实现有序列表","date":"2019-07-12T09:53:04.000Z","updated":"2019-07-16T10:06:05.404Z","comments":true,"path":"2019/07/12/Python实现有序列表/","link":"","permalink":"http://yoursite.com/2019/07/12/Python实现有序列表/","excerpt":"有序列表 有序列表中元素是按照从小到大顺序排列的。当我们考虑有序列表时,我们应该可以注意到isEmpty和size方法的实现和无序列表 相同,因为它们只处理列表中节点的数量而不考虑节点的实际值。需要改变的是index,remove,search和add,因为有序列表的有序性,当我们搜索到的节点值大于target时,即表示当前值不存在。","text":"有序列表 有序列表中元素是按照从小到大顺序排列的。当我们考虑有序列表时,我们应该可以注意到isEmpty和size方法的实现和无序列表 相同,因为它们只处理列表中节点的数量而不考虑节点的实际值。需要改变的是index,remove,search和add,因为有序列表的有序性,当我们搜索到的节点值大于target时,即表示当前值不存在。 有序列表操作 orderedList() add() remove() search() isEmpty() size() index(item) pop() pop(pos) python实现有序列表123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138# python实现有序列表# Node 类class Node(): def __init__(self, element): self.data = element self.next = None def getData(self): return self.data def getNext(self): return self.next def setData(self, newelement): self.data = newelement def setNext(self, newnext): self.next = newnext# 无序列表类class orderedList(): # unorderedList要保持对列表头一个节点的引用 def __init__(self): self.head = None # 判断链表是否为空,即列表头是否指向None def isEmpty(self): return self.head == None # 添加add()方法 def add(self, item): previous = None current = self.head stop = False while current != None and not stop: if current.getData() > item: stop = True else: previous = current current = current.getNext() newNode = Node(item) if previous == None: newNode.setNext(self.head) self.head = newNode else: newNode.setNext(current) previous.setNext(newNode) # 添加size()方法,此过程需要遍历链表 def size(self): current = self.head count = 0 while current != None: count += 1 current = current.getNext() return count # 添加search方法 def search(self, item): current = self.head found = False stop = False while current != None and not found and not stop: if current.getData() == item: found = True else: if current.getData() > item: stop = True else: current = current.getNext() return found # 添加remove()方法 def remove(self, item): if not self.search(item): return \"The unorderedList don't contain this item!\" previous = None current = self.head found = False while not found: if current.getData() == item: found = True else: previous = current current = current.getNext() # 要删除的节点为第一个节点 if previous == None: self.head = current.getNext() else: previous.setNext(current.getNext()) # 添加index()方法 def index(self, item): current = self.head count = -1 while current != None: count += 1 if current.getData() == item: return count current = current.getNext() return None # 添加pop()方法 def pop(self): previous = None current = self.head while current != None: previous = current current = current.getNext() previous.setNext(None) return previous.data # 添加insert()方法 def insert(self, pos, item): newNode = Node(item) previous = None current = self.head for i in range(pos): previous = current current = current.getNext() # in case of pos = 0 if previous == None: newNode.setNext(self.head) self.head = newNode else: previous.setNext(newNode) newNode.setNext(current) # traverse the unorderedList def println(self): current = self.head lists = [] while current != None: lists.append(current.data) current = current.getNext() print(lists) 链表的分析:当分析链表方法的复杂度时,我们应该考虑它们是否需要遍历链表。考虑一个有 n 个节点的链 表,isEmpty 方法复杂度是 O(1),因为它只需要检查链表的头指针是否为 None。对于方法 size, 则总需要 n 个步骤,因为除了遍历整个链表以外,没有办法知道链表的节点数。因此,size 方法的复 杂度是 O(n)。无序列表的 add 方法的复杂度是 O(1),因为我们永远只需要在链表的头部简单 地添加一个新的节点。但是,search、remove 和在有序列表中的 add 方法,需要遍历。尽管在平均 情况下,它们可能只需要遍历一半的节点,但这些方法的复杂度都是 O(n),因为在最糟糕的情况下 需要遍历整个链表。你可能还注意到,这些方法的实现性能与 Python 的内置列表 list 不同,这表明 Python 中的 list 不是这么实现的。实际上,Python 中的列表的实现是基于数组的。","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"LinkedList","slug":"LinkedList","permalink":"http://yoursite.com/tags/LinkedList/"},{"name":"Node","slug":"Node","permalink":"http://yoursite.com/tags/Node/"},{"name":"OrderedList","slug":"OrderedList","permalink":"http://yoursite.com/tags/OrderedList/"}]},{"title":"Python实现无序列表","slug":"Python实现无序列表","date":"2019-07-12T09:51:30.000Z","updated":"2019-07-16T10:04:25.590Z","comments":true,"path":"2019/07/12/Python实现无序列表/","link":"","permalink":"http://yoursite.com/2019/07/12/Python实现无序列表/","excerpt":"无序列表 无序列表由一个节点集合组成,每个节点采用显式引用链接到下一个节点。","text":"无序列表 无序列表由一个节点集合组成,每个节点采用显式引用链接到下一个节点。 无序列表操作 unorderedList() add() remove() search() isEmpty() size() append() index(item) insert(pos, item) pop() pop(pos)无序列表实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132# python实现无序列表# Node 类class Node(): def __init__(self, element): self.data = element self.next = None def getData(self): return self.data def getNext(self): return self.next def setData(self, newelement): self.data = newelement def setNext(self, newnext): self.next = newnext# 无序列表类class unorderedList(): # unorderedList要保持对列表头一个节点的引用 def __init__(self): self.head = None # 判断无序列是否为空,即列表头是否指向None def isEmpty(self): return self.head == None # 添加add()方法 def add(self, item): newNode = Node(item) newNode.setNext(self.head) self.head = newNode # 添加size()方法,此过程需要遍历无序列表 def size(self): current = self.head count = 0 while current != None: count += 1 current = current.getNext() return count # 添加search方法 def search(self, item): current = self.head found = False while current != None and not found: if current.getData() == item: found = True else: current = current.getNext() return found # 添加remove()方法 def remove(self, item): if not self.search(item): return \"The unorderedList don't contain this item!\" previous = None current = self.head found = False while not found: if current.getData() == item: found = True else: previous = current current = current.getNext() # 要删除的节点为第一个节点 if previous == None: self.head = current.getNext() else: previous.setNext(current.getNext()) # 添加append()方法 def append(self, item): newNode = Node(item) if self.head == None: newNode.setNext(self.head) self.head = newNode else: previous = None current = self.head while current != None: previous = current current = current.getNext() previous.setNext(newNode) # 添加index()方法 def index(self, item): current = self.head count = -1 while current != None: count += 1 if current.getData() == item: return count current = current.getNext() # 添加pop()方法 def pop(self): previous = None current = self.head while current != None: previous = current current = current.getNext() previous.setNext(None) return previous.data # 添加insert()方法 def insert(self, pos, item): newNode = Node(item) previous = None current = self.head for i in range(pos): previous = current current = current.getNext() # in case of pos = 0 if previous == None: newNode.setNext(self.head) self.head = newNode else: previous.setNext(newNode) newNode.setNext(current) # traverse the unorderedList def println(self): current = self.head lists = [] while current != None: lists.append(current.data) current = current.getNext() print(lists)","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"UnorderedList","slug":"UnorderedList","permalink":"http://yoursite.com/tags/UnorderedList/"},{"name":"LinkedList","slug":"LinkedList","permalink":"http://yoursite.com/tags/LinkedList/"},{"name":"Node","slug":"Node","permalink":"http://yoursite.com/tags/Node/"}]},{"title":"数据结构与算法(Python)","slug":"数据结构与算法-Python","date":"2019-07-10T14:39:37.000Z","updated":"2019-07-16T09:49:17.709Z","comments":true,"path":"2019/07/10/数据结构与算法-Python/","link":"","permalink":"http://yoursite.com/2019/07/10/数据结构与算法-Python/","excerpt":"栈 栈(stack)是一个项的有序集合,添加项和移除项都发生在同一端,这个端称为“顶”,另一端称为“底”。后添加的项会被首先移除,这种排序原则称为后进先出(LIFO)。 栈操作","text":"栈 栈(stack)是一个项的有序集合,添加项和移除项都发生在同一端,这个端称为“顶”,另一端称为“底”。后添加的项会被首先移除,这种排序原则称为后进先出(LIFO)。 栈操作 stack():创建一个新的空栈 stack.is_empty():判断是否为空 stack.push(item):元素入栈 stack.peek():元素出栈(不删除栈顶元素) stack.size():栈的大小 stack.pop():元素出栈(删除栈顶元素) Python实现栈12345678910111213141516171819202122232425# python list实现栈class Stack(): def __init__(self): self.items = [] def isEmpty(self): return self.items == [] def push(self, item): self.items.append(item) def peek(self): if self.isEmpty(): return \"The stack is empty!\" else: return self.items[-1] def size(self): return len(self.items) def pop(self): if self.isEmpty(): return \"The stack is empty!\" else: return self.items.pop() 队列 队列(queue)是一系列有顺序的元素的集合,新元素的加入在队列的“队尾”(rear),元素的移除发生在队列的“队首”(front)。这种排序原则叫做先进先出(FIFO)。 队列操作 queue.queue():创建一个空队列 queue.enqueue(item):添加元素到队尾 queue.dequeue():从队首移除元素 queue.isEmpty():判断队列是否为空 queue.size():队列大小 Python实现队列12345678910111213141516171819# python list实现队列class Queue(): def __init__(self): self.items = [] def isEmpty(self): return self.items == [] def enqueue(self, item): self.items.insert(0, item) def dequeue(self): if self.isEmpty(): return \"The queue is empty!\" else: return self.items.pop() def size(self): return len(self.items) 双端队列 双端队列(deque, double-ended queue)也是一系列元素的有序集合,其两端分别称为队首(front)和队尾(rear)。元素可以从两端插入,也可以从两端删除,兼具栈和队列的所有功能。 双端队列操作 Deque():创建双端队列 addFront(item):在队首插入元素 addRear(item):在队尾插入元素 rmFront():从队首移除元素 rmRear():从队尾移除元素 isEmpty():判断双端队列是否为空 size():返回双端队列的大小 Python list实现双端队列123456789101112131415161718192021222324252627class Deque(): def __init__(self): self.items = [] def isEmpty(self): return self.items == [] def addFront(self, item): self.items.append(item) def addRear(self, item): self.items.insert(0, item) def rmFront(self): if self.isEmpty(): return \"The deque is empty!\" else: return self.items.pop() def rmRear(self): if self.isEmpty(): return \"The deque is empty!\" else: return self.items.pop(0) def size(self): return len(self.items) 无序列表 无序列表由一个节点集合组成,每个节点采用显式引用链接到下一个节点。 无序列表操作 unorderedList() add() remove() search() isEmpty() size() append() index(item) insert(pos, item) pop() pop(pos)无序列表实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132# python实现无序列表# Node 类class Node(): def __init__(self, element): self.data = element self.next = None def getData(self): return self.data def getNext(self): return self.next def setData(self, newelement): self.data = newelement def setNext(self, newnext): self.next = newnext# 无序列表类class unorderedList(): # unorderedList要保持对列表头一个节点的引用 def __init__(self): self.head = None # 判断无序列是否为空,即列表头是否指向None def isEmpty(self): return self.head == None # 添加add()方法 def add(self, item): newNode = Node(item) newNode.setNext(self.head) self.head = newNode # 添加size()方法,此过程需要遍历无序列表 def size(self): current = self.head count = 0 while current != None: count += 1 current = current.getNext() return count # 添加search方法 def search(self, item): current = self.head found = False while current != None and not found: if current.getData() == item: found = True else: current = current.getNext() return found # 添加remove()方法 def remove(self, item): if not self.search(item): return \"The unorderedList don't contain this item!\" previous = None current = self.head found = False while not found: if current.getData() == item: found = True else: previous = current current = current.getNext() # 要删除的节点为第一个节点 if previous == None: self.head = current.getNext() else: previous.setNext(current.getNext()) # 添加append()方法 def append(self, item): newNode = Node(item) if self.head == None: newNode.setNext(self.head) self.head = newNode else: previous = None current = self.head while current != None: previous = current current = current.getNext() previous.setNext(newNode) # 添加index()方法 def index(self, item): current = self.head count = -1 while current != None: count += 1 if current.getData() == item: return count current = current.getNext() # 添加pop()方法 def pop(self): previous = None current = self.head while current != None: previous = current current = current.getNext() previous.setNext(None) return previous.data # 添加insert()方法 def insert(self, pos, item): newNode = Node(item) previous = None current = self.head for i in range(pos): previous = current current = current.getNext() # in case of pos = 0 if previous == None: newNode.setNext(self.head) self.head = newNode else: previous.setNext(newNode) newNode.setNext(current) # traverse the unorderedList def println(self): current = self.head lists = [] while current != None: lists.append(current.data) current = current.getNext() print(lists) 有序列表 有序列表中元素是按照从小到大顺序排列的。当我们考虑有序列表时,我们应该可以注意到isEmpty和size方法的实现和无序列表 相同,因为它们只处理列表中节点的数量而不考虑节点的实际值。需要改变的是index,remove,search和add,因为有序列表的有序性,当我们搜索到的节点值大于target时,即表示当前值不存在。 有序列表操作 orderedList() add() remove() search() isEmpty() size() index(item) pop() pop(pos) python实现有序列表123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138# python实现有序列表# Node 类class Node(): def __init__(self, element): self.data = element self.next = None def getData(self): return self.data def getNext(self): return self.next def setData(self, newelement): self.data = newelement def setNext(self, newnext): self.next = newnext# 无序列表类class orderedList(): # unorderedList要保持对列表头一个节点的引用 def __init__(self): self.head = None # 判断链表是否为空,即列表头是否指向None def isEmpty(self): return self.head == None # 添加add()方法 def add(self, item): previous = None current = self.head stop = False while current != None and not stop: if current.getData() > item: stop = True else: previous = current current = current.getNext() newNode = Node(item) if previous == None: newNode.setNext(self.head) self.head = newNode else: newNode.setNext(current) previous.setNext(newNode) # 添加size()方法,此过程需要遍历链表 def size(self): current = self.head count = 0 while current != None: count += 1 current = current.getNext() return count # 添加search方法 def search(self, item): current = self.head found = False stop = False while current != None and not found and not stop: if current.getData() == item: found = True else: if current.getData() > item: stop = True else: current = current.getNext() return found # 添加remove()方法 def remove(self, item): if not self.search(item): return \"The unorderedList don't contain this item!\" previous = None current = self.head found = False while not found: if current.getData() == item: found = True else: previous = current current = current.getNext() # 要删除的节点为第一个节点 if previous == None: self.head = current.getNext() else: previous.setNext(current.getNext()) # 添加index()方法 def index(self, item): current = self.head count = -1 while current != None: count += 1 if current.getData() == item: return count current = current.getNext() return None # 添加pop()方法 def pop(self): previous = None current = self.head while current != None: previous = current current = current.getNext() previous.setNext(None) return previous.data # 添加insert()方法 def insert(self, pos, item): newNode = Node(item) previous = None current = self.head for i in range(pos): previous = current current = current.getNext() # in case of pos = 0 if previous == None: newNode.setNext(self.head) self.head = newNode else: previous.setNext(newNode) newNode.setNext(current) # traverse the unorderedList def println(self): current = self.head lists = [] while current != None: lists.append(current.data) current = current.getNext() print(lists) 链表的分析:当分析链表方法的复杂度时,我们应该考虑它们是否需要遍历链表。考虑一个有 n 个节点的链 表,isEmpty 方法复杂度是 O(1),因为它只需要检查链表的头指针是否为 None。对于方法 size, 则总需要 n 个步骤,因为除了遍历整个链表以外,没有办法知道链表的节点数。因此,size 方法的复 杂度是 O(n)。无序列表的 add 方法的复杂度是 O(1),因为我们永远只需要在链表的头部简单 地添加一个新的节点。但是,search、remove 和在有序列表中的 add 方法,需要遍历。尽管在平均 情况下,它们可能只需要遍历一半的节点,但这些方法的复杂度都是 O(n),因为在最糟糕的情况下 需要遍历整个链表。你可能还注意到,这些方法的实现性能与 Python 的内置列表 list 不同,这表明 Python 中的 list 不是这么实现的。实际上,Python 中的列表的实现是基于数组的。 递归 递归是一种解决问题的方法,它把一个问题分解为越来越小的子问题,直到问题的规模小到 可以被很简单直接解决。而递归函数就是一种调用自身的函数。 递归求和1234567891011# 列表递归求和方法一def listSum(lst): if len(lst) == 1: return lst[0] else: return lst[0] + listSum(lst[1:])# 列表递归求和方法二def iterSum(arr): head, *tails = arr return head + iterSum(tails) if tails else head 递归三大定律 递归算法必须要递归地调用自己 递归算法必须有一个基本结束条件 递归算法必须改变自己的状态并向基本结束条件靠近1234567# 递归转换整数到字符串def to_str(n, str): convert_str = '0123456789ABCDEF' if n < base: return convert_str[n] else: return to_str(n//base, base) + convert_str[n%base] 123456# 写一个函数,接受一个字符串作为参数,并返回一个反向的新字符串。def reverse(s): if len(s) == 1: return s else: return reverse(s[1:]) + s[0] 总结: 递归的逻辑就是利用优美的程序把问题分解为更小更简单的子问题来解决。 谢尔宾斯基三角形汉诺塔问题动态规划 搜索 在Python中,有一个非常简单的方法来判别特定项是否在列表中。我们使用in这个运算符。但为了说明这些搜索算法的工作原理以及它们相互比较时的优劣之分,我们还是自己实现这些算法。 无序列表的顺序搜索123456789def sequentialSearch(alist, item): pos = 0 found = False while pos < len(alist) and not found: if alist[pos] == item: found = True else: pos += 1 return found 有序列表的顺序搜索123456789101112def orderedSequentialSearch(alist, item): pos = 0 found = False stop = False while pos < len(alist) and not found and not stop: if alist[pos] == item: found = True elif alist[pos] > item: stop = True else: pos += 1 return found 二分法搜索1234567891011121314# 二分法搜索返回True或Falsedef binarySearch(alist, item): head = 0 tail = len(alist) - 1 found = False while head <= tail and not found: mid = (head + tail)//2 if alist[mid] == item: found = True elif alist[mid] < item: head = mid + 1 else: tail = mid - 1 return found 12345678910111213# 二分法搜索返回indexdef binarySearch2(alist, item): head = 0 tail = len(alist) - 1 while head <= tail: mid = (head + tail)//2 if alist[mid] == item: return mid elif alist[mid] < item: head = mid + 1 else: tail = mid - 1 return \"The item '{}' is not found!\".format(item) 123456789101112# 二分法递归搜索,返回True或Falsedef binarySearch3(alist, item): if len(alist) == 0: return \"The item '{}' is not found!\".format(item) else: mid = len(alist)//2 if alist[mid] == item: return True elif alist[mid] < item: return binarySearch3(alist[mid+1:], item) else: return binarySearch3(alist[:mid], item) 1234567891011121314151617# 二分法递归搜索,返回indexdef binarySearch3(alist, item, head, tail): ''' 因为返回值是index,所以每次递归传回的列表必须是原列表, 这样才能保证index是在原列表中的index,而不是每次递归 缩小的列表的index ''' if head > tail: return \"The item '{}' is not found!\".format(item) else: mid = (head + tail)//2 if alist[mid] == item: return True elif alist[mid] < item: return binarySearch3(alist, item, mid+1, tail) else: return binarySearch3(alist, item, head, mid-1) 散列表 散列表是一种数据的集合,其中的每个数据都通过某种特定的方式进行存储以方面日后的查找。基于它的搜索算法的时间复杂度为O(1)。散列表的每一个位置叫做槽,能够存放一个数据项,并以从0开始递增的整数命名。例如, 第一个槽记为0,下一个记为1,再下一个记为2,并以此类推。在初始条件下,散列表中是没有任何数据的,即每个槽都是空的。某个数据项与在散列表中存储它的槽之间的映射叫做散列函数。一般地,我们把槽被占据的比例叫做负载因子,两个甚至多个数据需要存储在同一个槽中,这种情况被称为冲突。 散列函数对于一组给定的数据项,如果一个散列函数可以将每一个数据项都映射到不同的槽中,那么这样的散列函数叫做完美散列函数。一种实现完美散列函数的方法就是扩大散列表的尺寸,直到所有可能的数据项变化范围都被散列表所包含。这一方法保证了每一个数据项都有一个不同的槽。尽管这个方法对于少量数据是可行的,但是当它面对大量可能的数据项时显得捉襟见肘。我们的目标是创建一个能够将冲突的数量降到最小,计算方便,并且最终将数据项分配到散 列表中的散列函数。有几种普通的方法去扩展求余方法: 折叠法:首先将数据分成相同长度的片段(最后一个片段长度可能不等)。接着将这些片段相加,再求余得到其散列值。 平方取中法:我们首先将数据取平方,然后取平方数的某一部分,然后再进行求余运算。我们也可以为非数字的数据项,例如字符串创建散列表,’cat’可以看做一个连续的ASCII数值。123456# 用ASCII数值散列一个字符串def hash(astring, tablesize): sum = 0 for i in range(len(astring)): sum = sum + ord(astring[i]) return sum%tablesize 当我们用散列函数纪录散列值的时候,颠倒的字母构成的单词会得到相同的散列值。为了纠正这一情况,我们可以将字母的位置作为权重因子。123456# 用ASCII数值*权重散列一个字符串def hash(astring, tablesize): sum = 0 for i in range(len(astring)): sum = sum + ord(astring[i])*(i+1) return sum%tablesize 冲突当两个数据散列到相同的槽,我们必须用一种系统化的方法将第二个数据放到散列表里。这个过程叫做冲突解决。 一种解决冲突的方法就是搜索散列表并寻找另一个空的槽来存放这个有冲突的数据。一种简单的方法就是从发生冲突的位置开始顺序向下开始寻找,直到我们遇到第一个空的槽。注意到我们可能需要回到第一个槽(循环)来实现覆盖整个散列表。这种冲突解决方法叫做开放地址,它试图在散列表中去寻找下一个空的槽。通过系统地向后搜索每一个槽,我们将这种实现开放地址的技术叫做线性探测。 线性探测法的一个缺点是产生集中的趋势:数据会在表中聚集。这意味着如果对于同一散列值产生了许多冲突时,周边的一系列槽都将会被线性探测填充。这将会对正在被填入的新数据产生影响。一种解决集中问题的方法是扩展线性探测技术,我们不再按顺序一个一个地寻找新槽,而是跳过一些槽,这样能更加平均地分配出现冲突的数据,进而潜在地减少集中问题出现的次数。下图展示了同样的数据项如何通过“+3”线性探测的方法解决冲突的。“+3”表示一旦一个冲突出现,我们将每次跳过两个槽来寻找下一个新的空槽。 另一种线性探测方法叫做二次探测法。我们不是每次在冲突中选择跳过固定个数的槽,而是使用一个再散列函数使每次跳过槽的数量会依次增加1,3,5,7,9,以此类推。这意味着如果原槽为第h个,那么再散列时访问的槽为第h+1,h+4,h+9,h+16个,以此类推。换言之,二次探测法使用一个连续的完全平方数数列作为它的跳跃值。图11显示了我们的例子在运用二次探测法时的 填充结果。 另一个解决冲突的替代方法是允许每一个槽都能填充一串而不是一个数据(称作链)。链能允许多个数据填在散列表中的同一个位置上。当冲突发生时,数据还是填在本应该位于的槽中。 随着一个槽中填入的数据的增多,搜索的难度也就随之增加。下图显示了数据在用数据链方法填入散列表的结果。 实现映射的抽象数据类型 字典是Python中最有用的数据类型之一。字典是一个可以储存键-值对的关联数据类型。键是用来查找和它相关联的值的。通常把这个想法称作映射。映射的抽象数据类型定义如下:它以一个键与一个值之间关联的无序集合作为结构。映射中的键都是独特的,以保证和值之间的一一对应关系。 映射的相关操作: Map():产生一个新的空映射 put():往映射中添加键-值对。如果密钥已经存在,那么将旧的数据值置换为新的。 get(key):给定一个 key 值,返回关联的数据,若不存在,返回None。 del:从映射中删除一个密钥-数据值对,声明形式为del map[key]。 len():返回映射中的存储密钥-数据值对的个数。 in:表述为key in map,如果给定的键在map中返回True,否则返回False。 python实现映射12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758# 创建哈希表类class Hashtable(): def __init__(self): self.size = 11 # 定义两个列表分别存储键和值 self.slots = [None]*self.size self.data = [None]*self.size # 定义put()方法 def put(self, key, data): hashvalue = self.hashFunc(key, len(self.slots)) # hashvalue为计算出的一个整数,此处相当于列表的下标 if self.slots[hashvalue] == None: # 创建新的键值对 self.slots[hashvalue] = key self.data[hashvalue] = data else: if self.slots[hashvalue] == key: # 该键的位置没有被rehash过,重新赋予键新值 print('***', hashvalue, self.slots[hashvalue], key) self.data[hashvalue] = data else: # 该键的位置被rehash过,重新赋予键新值 nextslot = self.rehash(hashvalue, len(self.slots)) while self.slots[nextslot] != None and self.slots[nextslot] != key: nextslot = self.rehash(nextslot, len(self.slots)) if self.slots[nextslot] == None: self.slots[nextslot] = key self.data[nextslot] = data else: self.data[nextslot] = data # 定义hashFunc()方法 def hashFunc(self, key, size): return key%size # 定义rehash()方法 def rehash(self, oldvalue, size): return (oldvalue + 1)%size # 定义get()方法 def get(self, key): initslot = self.hashFunc(key, len(self.slots)) data = None stop = False found = False pos = initslot while self.slots[pos] != None and not found and not stop: if self.slots[pos] == key: found = True data = self.data[pos] else: pos = self.rehash(pos, len(self.slots)) if pos == initslot: # 循环到开始处,表示没有搜索到 stop = True return data def __getitem__(self, key): return self.get(key) def __setitem__(self, key, data): self.put(key, data) 持续更新中…","categories":[],"tags":[{"name":"Queue","slug":"Queue","permalink":"http://yoursite.com/tags/Queue/"},{"name":"Stack","slug":"Stack","permalink":"http://yoursite.com/tags/Stack/"},{"name":"Data Structure","slug":"Data-Structure","permalink":"http://yoursite.com/tags/Data-Structure/"},{"name":"Algorithm","slug":"Algorithm","permalink":"http://yoursite.com/tags/Algorithm/"},{"name":"dequeue","slug":"dequeue","permalink":"http://yoursite.com/tags/dequeue/"},{"name":"unorderedList","slug":"unorderedList","permalink":"http://yoursite.com/tags/unorderedList/"},{"name":"orderedList","slug":"orderedList","permalink":"http://yoursite.com/tags/orderedList/"}]},{"title":"conda常用命令总结","slug":"conda常用命令总结","date":"2019-07-10T13:59:58.000Z","updated":"2019-08-02T07:29:47.579Z","comments":true,"path":"2019/07/10/conda常用命令总结/","link":"","permalink":"http://yoursite.com/2019/07/10/conda常用命令总结/","excerpt":"Conda是一个开源跨平台的包管理以及环境管理系统。它可以快速地安装、运行和更新软件包及其依赖,并很简单的创建、克隆、切换、清除虚拟环境","text":"Conda是一个开源跨平台的包管理以及环境管理系统。它可以快速地安装、运行和更新软件包及其依赖,并很简单的创建、克隆、切换、清除虚拟环境 环境管理1234567conda info -e # 查看当前已经安装的环境,当前激活的环境会显示一个'*',不同环境都在目录$HOME/.anaconda/envs下conda create --name python37 python=3.7 # 创建python37环境,python版本为3.7,但是此时仅安装python3.7的必须项(如python, pip等), 如果希望该环境像默认环境一样,需要执行conda install anacondaconda activate python37 # 激活环境(conda4.4以前的版本为source activate python37)conda deactivate python37 # 返回base环境conda remove --name python37 --all # 删除已有环境conda create -n flowers --clone snowflakes # 通过克隆snowfllakes来创建一个称为flowers的副本conda info -envis # 确认当前环境 包管理123456789101112131415conda install numpy # 安装numpyconda list # 用于查看当前环境下各种方式安装的包conda list -n python37 # 用于查看指定环境下已安装的包conda search numpy # 查看包信息conda install -n python37 numpy # 安装在指定环境conda update -n python37 numpy # 更新指定环境下的包conda remove -n python37 numpy # 删除特定环境下的包conda update conda # 更新condaconda update anaconda # 更新anacondaconda update python # 更新pythonconda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ # 添加anaconda镜像conda config --set show_channel_urls yes # 设置搜索时显示通道地址anaconda search -t conda packagename # 查找想要的包anaconda show USER/packagename # 查看包的详细信息conda install --channel http://site packagename # 指定 channel 进行安装","categories":[],"tags":[{"name":"Anaconda","slug":"Anaconda","permalink":"http://yoursite.com/tags/Anaconda/"},{"name":"conda","slug":"conda","permalink":"http://yoursite.com/tags/conda/"}]},{"title":"Python实现队列","slug":"Python实现队列","date":"2019-07-10T09:49:41.000Z","updated":"2019-07-16T10:03:37.331Z","comments":true,"path":"2019/07/10/Python实现队列/","link":"","permalink":"http://yoursite.com/2019/07/10/Python实现队列/","excerpt":"队列 队列(queue)是一系列有顺序的元素的集合,新元素的加入在队列的“队尾”(rear),元素的移除发生在队列的“队首”(front)。这种排序原则叫做先进先出(FIFO)。","text":"队列 队列(queue)是一系列有顺序的元素的集合,新元素的加入在队列的“队尾”(rear),元素的移除发生在队列的“队首”(front)。这种排序原则叫做先进先出(FIFO)。 队列操作 queue.queue():创建一个空队列 queue.enqueue(item):添加元素到队尾 queue.dequeue():从队首移除元素 queue.isEmpty():判断队列是否为空 queue.size():队列大小 Python实现队列12345678910111213141516171819# python list实现队列class Queue(): def __init__(self): self.items = [] def isEmpty(self): return self.items == [] def enqueue(self, item): self.items.insert(0, item) def dequeue(self): if self.isEmpty(): return \"The queue is empty!\" else: return self.items.pop() def size(self): return len(self.items) 双端队列 双端队列(deque, double-ended queue)也是一系列元素的有序集合,其两端分别称为队首(front)和队尾(rear)。元素可以从两端插入,也可以从两端删除,兼具栈和队列的所有功能。 双端队列操作 Deque():创建双端队列 addFront(item):在队首插入元素 addRear(item):在队尾插入元素 rmFront():从队首移除元素 rmRear():从队尾移除元素 isEmpty():判断双端队列是否为空 size():返回双端队列的大小 Python list实现双端队列123456789101112131415161718192021222324252627class Deque(): def __init__(self): self.items = [] def isEmpty(self): return self.items == [] def addFront(self, item): self.items.append(item) def addRear(self, item): self.items.insert(0, item) def rmFront(self): if self.isEmpty(): return \"The deque is empty!\" else: return self.items.pop() def rmRear(self): if self.isEmpty(): return \"The deque is empty!\" else: return self.items.pop(0) def size(self): return len(self.items)","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"Queue","slug":"Queue","permalink":"http://yoursite.com/tags/Queue/"},{"name":"deque","slug":"deque","permalink":"http://yoursite.com/tags/deque/"}]},{"title":"Python实现栈","slug":"Python实现栈","date":"2019-07-10T09:48:11.000Z","updated":"2019-07-16T10:03:25.079Z","comments":true,"path":"2019/07/10/Python实现栈/","link":"","permalink":"http://yoursite.com/2019/07/10/Python实现栈/","excerpt":"栈 栈(stack)是一个项的有序集合,添加项和移除项都发生在同一端,这个端称为“顶”,另一端称为“底”。后添加的项会被首先移除,这种排序原则称为后进先出(LIFO)。 栈操作","text":"栈 栈(stack)是一个项的有序集合,添加项和移除项都发生在同一端,这个端称为“顶”,另一端称为“底”。后添加的项会被首先移除,这种排序原则称为后进先出(LIFO)。 栈操作 stack():创建一个新的空栈 stack.is_empty():判断是否为空 stack.push(item):元素入栈 stack.peek():元素出栈(不删除栈顶元素) stack.size():栈的大小 stack.pop():元素出栈(删除栈顶元素) Python实现栈12345678910111213141516171819202122232425# python list实现栈class Stack(): def __init__(self): self.items = [] def isEmpty(self): return self.items == [] def push(self, item): self.items.append(item) def peek(self): if self.isEmpty(): return \"The stack is empty!\" else: return self.items[-1] def size(self): return len(self.items) def pop(self): if self.isEmpty(): return \"The stack is empty!\" else: return self.items.pop()","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"Stack","slug":"Stack","permalink":"http://yoursite.com/tags/Stack/"}]},{"title":"Navicat 连接 Mysql 8.0.16报错解决","slug":"Navicat-连接-Mysql-8-0-16报错解决","date":"2019-07-08T12:21:13.000Z","updated":"2019-07-08T12:29:44.265Z","comments":true,"path":"2019/07/08/Navicat-连接-Mysql-8-0-16报错解决/","link":"","permalink":"http://yoursite.com/2019/07/08/Navicat-连接-Mysql-8-0-16报错解决/","excerpt":"错误提示:Client does not support authentication protocol requested by server; consider upgrading MySQL client","text":"错误提示:Client does not support authentication protocol requested by server; consider upgrading MySQL client 问题出现原因MySQL8.0与MySQL5.0所采用的加密方式规则不一样,所以导致Navicat打不开。可通过select host, user, authentication_string, plugin from user;查看密码的规则。root用户的加密规则默认的是:caching_sha2_password,所以我们只需要将默认的caching_sha2_password改为mysql_native_password即可。 解决方法123mysql -u root -p,输入密码ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'yourpassword';FLUSH PRIVILEGES;","categories":[],"tags":[{"name":"MySQL","slug":"MySQL","permalink":"http://yoursite.com/tags/MySQL/"},{"name":"Navicat","slug":"Navicat","permalink":"http://yoursite.com/tags/Navicat/"}]},{"title":"SQL学习笔记","slug":"SQL学习笔记","date":"2019-07-08T08:41:40.000Z","updated":"2019-07-25T15:03:55.583Z","comments":true,"path":"2019/07/08/SQL学习笔记/","link":"","permalink":"http://yoursite.com/2019/07/08/SQL学习笔记/","excerpt":"什么是SQLSQL是用于访问和处理数据库的标准的计算机语言 结构化查询语言,全称是structured Query Language 可以访问和处理数据库 是ANSI标准的计算机语言 除了SQL标准之外, 大部分SQL数据库程序都拥有它们自己的专有扩展","text":"什么是SQLSQL是用于访问和处理数据库的标准的计算机语言 结构化查询语言,全称是structured Query Language 可以访问和处理数据库 是ANSI标准的计算机语言 除了SQL标准之外, 大部分SQL数据库程序都拥有它们自己的专有扩展 关系数据库 关系数据库 = 多张表 + 各表之间的关系表的结构 表名 列和列名 行 主键:标注数据唯一性各表之间的关系 联结数据库的数据类型 字符型 数字 日期SQL分类1. DDL(数据定义语言) create: 创建数据库和表 drop: 删除数据库和表 alter: 修改数据库和表的结构如:1234567create table students(学号 varchar(20),姓名 varchar(20) not null,出生日期 date not null,性别 varchar(20) not null,primary key(学号)); 2. DML(数据操作语言) insert into: 向表中插入数据 delete: 删除表中的数据 select: 查询表中的数据 update: 修改表中的数据3. DCL(数据控制语言) commit: 确认对数据库中的数据进行的变更 rollback: 取消对数据库中的数据进行的变更 crant: 赋予用户操作的权限SQL语句书写规则 SQL语句以英文分号”;”结尾 SQL语句不区分关键字的大小写 输入字符的时候只能使用英文字符如:1insert into student (学号,姓名,出生日期,性别) values ('0001','小明', '2000-06-06', '男'); 常用命令及例句1234567891011121314151617181920212223242526272829303132333435363738394041-- 常用命令create databases -- 创建新数据库alter databases -- 修改数据库create table -- 创建新表alter table -- 更改数据库表drop table -- 删除数据表create index -- 创建索引drop index -- 删除索引select -- 提取数据update -- 更新数据delete -- 删除数据insert into -- 插入数据-- 例句use school; -- 选择数据库set name utf8; -- 设置使用的字符集select * from school.student; -- 读取数据表信息select 学号,姓名 from student;select distinct column_name, column_name from table_name; -- 仅列出不相同的值select column_name, column_name from table_name where 性别='男'; -- 用于提取那些满足指定条件的记录,如提取所有男生信息Select * from emp where sal > 2000 and sal < 3000;Select * from emp where sal > 2000 or comm > 500;select * from emp where not sal > 1500;Select * from emp where comm is null;Select * from emp where sal between 1500 and 3000;Select * from emp where sal in (5000,3000,1500);Select * from emp where ename like 'M%';select column_name, column_namefrom table_nameorder by column_name, column_name ASC|DESC; -- 对提取结果排序(DESC降序排列)insert into table_namevalues (value1, value2, value3, ...); -- 向表中插入数据insert into table_name (column1, column2, ...)values (value1, value2, ...); -- 指定列插入数据update table_nameset column1=value1, column2=value2where name='xxx'; -- 当名字为'xxx'时,更新该两列数值,执行update时一定要仔细检查where条件delete from table_namewhere some_column=some_value; -- 根据where条件删除数据,执行delete时一定要仔细检查where条件delete from table_name; -- 删除表中所有行,但表结构、属性、索引不变,表还在delete * from tabel_name; -- 删除表中所有行,但表结构、属性、索引不变,表还在show global variables like \"%datadir%\"; --查看数据库的物理地址 补充: 持续更新中…","categories":[],"tags":[{"name":"SQL","slug":"SQL","permalink":"http://yoursite.com/tags/SQL/"}]},{"title":"Logistic Regression","slug":"Logistic-Regression","date":"2019-06-14T04:42:32.000Z","updated":"2019-06-14T06:20:08.230Z","comments":true,"path":"2019/06/14/Logistic-Regression/","link":"","permalink":"http://yoursite.com/2019/06/14/Logistic-Regression/","excerpt":"在分类问题中,要预测的 $y$ 值离散的,逻辑回归是目前最流行使用最广泛的一种用于分类的学习算法。 Hypothesis 分类:$y = 0\\ or\\ 1$ 在逻辑回归中,$0\\ <\\ h_\\theta\\ <1 $, $h_\\theta = g(\\theta^TX)$,其中$X$ 表示特征向量,$g(z)=\\frac{1}{1+e^{-z}}$ 是一个常用的S型$(Sigmoid Function)$ 逻辑函数。","text":"在分类问题中,要预测的 $y$ 值离散的,逻辑回归是目前最流行使用最广泛的一种用于分类的学习算法。 Hypothesis 分类:$y = 0\\ or\\ 1$ 在逻辑回归中,$0\\ <\\ h_\\theta\\ <1 $, $h_\\theta = g(\\theta^TX)$,其中$X$ 表示特征向量,$g(z)=\\frac{1}{1+e^{-z}}$ 是一个常用的S型$(Sigmoid Function)$ 逻辑函数。 12345678910111213141516171819202122import numpy as np# sigmoid functiondef sigmoidFunc(z): return 1.0 / (1.0 + np.exp(-z))# set axesdef originset(): ax = plt.gca() ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') ax.xaxis.set_ticks_position('bottom') ax.yaxis.set_ticks_position('left') ax.spines['bottom'].set_position(('data', 0)) ax.spines['left'].set_position(('data', 0))originset()z = np.arange(-10, 10, 0.1)plt.plot(x, sigmoidFunc(x), 'b')plt.yticks(np.arange(0.2, 1.2, 0.2))plt.title('$Sigmoid\\ Function$') 对$h_\\theta$ 的理解: 在逻辑回归中,当$h_\\theta(x) >= 0.5$ 时,$y = 1$;当$h_\\theta(x) < 0.5$ 时,$y = 0$。 对于一个输入,根据选定的参数计算输出变量为1的可能性,即$h_{\\theta}(x)=P(y=1 | x ; \\theta)$。例如,如果对于给定的$x$,通过已经确定的参数计算得出$h_\\theta(x) = 0.7$,则表示有$70\\%$ 的几率$y$ 为正向类,相应地$y$ 为负向类的几率为$1-0.7=0.3$。 Cost Function 对于线性回归,可以使用模型误差的平方和作为代价函数$J(\\theta)$,在此基础上最小化$J(\\theta)$ 以求得$\\theta $,此时的代价函数是一个凸函数,没有局部最小值,可以很容易的找到全局最小值。但对于逻辑回归模型来说,若按照这样的思路来定义代价函数,此时的代价函数是非凸函数,有就很多的局部极小值,不利于梯度下降法的求解。 于是需要根据逻辑回归的特征重新定义代价函数:$J(\\theta)=\\frac{1}{m} \\sum_{i=1}^{m} \\operatorname{cost}\\left(h_{\\theta}\\left(x^{(i)}\\right), y^{(i)}\\right)$。其中$$\\begin{equation}\\operatorname{cost}\\left(h_{\\theta}(x), y\\right)=\\left{\\begin{aligned}-\\log \\left(h_{\\theta}(x)\\right) & \\text { if } y=1 \\\\-\\log \\left(1-h_{\\theta}(x)\\right) & \\text { if } y=0 \\end{aligned}\\right.\\end{equation}$$ $h_\\theta(x)$ 与$\\operatorname{cost}\\left(h_{\\theta}\\left(x^{(i)}\\right), y^{(i)}\\right)$ 的关系如下图所示: 从上图可以看出$\\operatorname{cost}\\left(h_{\\theta}\\left(x^{(i)}\\right), y^{(i)}\\right)$ 的特点:当$y = 1$时,$h_\\theta$ 越接近$1$,$\\operatorname{cost}\\left(h_{\\theta}\\left(x^{(i)}\\right), y^{(i)}\\right)$ 越小,反之越大;当$y = 0$时,$h_\\theta$ 越接近 $0$,$\\operatorname{cost}\\left(h_{\\theta}\\left(x^{(i)}\\right), y^{(i)}\\right)$ 越小,反之越大。 根据这一特点,构建$\\operatorname{cost}\\left(h_{\\theta}\\left(x^{(i)}\\right), y^{(i)}\\right)$ 如下:$\\operatorname{cost}\\left(h_{\\theta}(x), y\\right)=-y \\times \\log \\left(h_{\\theta}(x)\\right)-(1-y) \\times \\log \\left(1-h_{\\theta}(x)\\right)$。 据此可得代价函数:$J(\\theta)=\\frac{1}{m} \\sum_{i=1}^{m}\\left[-y^{(i)} \\log \\left(h_{\\theta}\\left(x^{(i)}\\right)\\right)-\\left(1-y^{(i)}\\right) \\log \\left(1-h_{\\theta}\\left(x^{(i)}\\right)\\right)\\right]$。 123456789# cost functiondef costfn(theta, X, y, mylambda = 0): term1 = np.dot(-y.T, np.log(h(theta, X))) term2 = np.dot((1 - y).T, np.log(1 - h(theta, X)))# regularized termregn = (mylambda/ (2.0 * m)) * np.sum(np.power(theta[1:], 2))return float(1.0/m * np.sum(term1 - term2) + regn) Gradient在得到代价函数之后,我们便可以利用梯度下降法来求解使代价函数最小的参数$\\theta$。 求解算法:Repeat $\\theta_{j} :=\\theta_{j}-\\alpha \\frac{\\partial}{\\partial \\theta_{j}} J(\\theta)$ and simultaneously update all $\\theta_j$ 求导后带入可得:Repeat $\\theta_{j} :=\\theta_{j}-\\alpha \\frac{1}{m} \\sum_{i=1}^{m}\\left(h_{\\theta}\\left(\\mathrm{x}^{(i)}\\right)-\\mathrm{y}^{(i)} \\quad\\right) \\mathrm{x}_{j}^{(i)} $ and simultaneously updata all $\\theta_j$ Summary主要公式: Sigmoid function: $g(z)=\\frac{1}{1+e^{-z}}$ Hypothesis: $h_\\theta = g(\\theta^TX) = g(z)=\\frac{1}{1+e^{-\\theta^TX}}$ $\\operatorname{cost}:$ $\\operatorname{cost}\\left(h_{\\theta}(x), y\\right)=-y \\times \\log \\left(h_{\\theta}(x)\\right)-(1-y) \\times \\log \\left(1-h_{\\theta}(x)\\right)$ Cost function: $J(\\theta)=\\frac{1}{m} \\sum_{i=1}^{m}\\left[-y^{(i)} \\log \\left(h_{\\theta}\\left(x^{(i)}\\right)\\right)-\\left(1-y^{(i)}\\right) \\log \\left(1-h_{\\theta}\\left(x^{(i)}\\right)\\right)\\right]$ Gradient: $\\frac{\\partial J(\\theta)}{\\partial \\theta_{j}}=\\frac{1}{m} \\sum_{i=1}^{m}\\left[h_{\\theta}\\left(x^{(i)}\\right)-y^{(i)}\\right] x_{j}^{(i)}$ Appendix:the deviation of $J (\\theta)$$$J(\\theta)=-\\frac{1}{m} \\sum_{i=1}^{m}\\left[y^{(i)} \\log \\left(h_{\\theta}\\left(x^{(i)}\\right)\\right)+\\left(1-y^{(i)}\\right) \\log \\left(1-h_{\\theta}\\left(x^{(i)}\\right)\\right)\\right]$$ 由于$h_{\\theta}\\left(x^{(i)}\\right)=\\frac{1}{1+e^{-\\theta^{T} x^{(i)}}}$,则:$$\\begin{array}{l}{y^{(i)} \\log \\left(h_{\\theta}\\left(x^{(i)}\\right)\\right)+\\left(1-y^{(i)}\\right) \\log \\left(1-h_{\\theta}\\left(x^{(i)}\\right)\\right)} \\\\ {=y^{(i)} \\log \\left(\\frac{1}{1+e^{-\\theta^{T} x^{(i)}}}\\right)+\\left(1-y^{(i)}\\right) \\log \\left(1-\\frac{1}{1+e^{-\\theta^{T} x^{(i)}}}\\right)} \\\\ {=-y^{(i)} \\log \\left(1+e^{-\\theta^{T} x^{(i)}}\\right)-\\left(1-y^{(i)}\\right) \\log \\left(1+e^{\\theta^{T} x^{(i)}}\\right)}\\end{array}$$ 所以,$$\\frac{\\partial}{\\partial \\theta_{j}} J(\\theta)=\\frac{\\partial}{\\partial \\theta_{j}}\\left[-\\frac{1}{m} \\sum_{i=1}^{m}\\left[-y^{(i)} \\log \\left(1+e^{-\\theta^{T} x^{(i)}}\\right)-\\left(1-y^{(i)}\\right) \\log \\left(1+e^{\\theta^{T} x^{(i)}}\\right)\\right]\\right]$$ $$\\begin{array}{l}{=-\\frac{1}{m} \\sum_{i=1}^{m}\\left[-y^{(i)} \\frac{-x_{j}^{(i)} e^{-\\theta^{T} x^{(i)}}}{1+e^{-\\theta^{T} x^{(i)}}}-\\left(1-y^{(i)}\\right) \\frac{x_{j}^{(i)} e^{\\theta^{T} x^{(i)}}}{1+e^{\\theta^{T} x^{(i)}}}\\right]} \\\\ {=-\\frac{1}{m} \\sum_{i=1}^{m} y^{(i)} \\frac{x_{j}^{(i)}}{1+e^{\\theta^{T} x^{(i)}}}-\\left(1-y^{(i)}\\right) \\frac{x_{j}^{(i)} e^{\\theta^{T} x^{(i)}}}{1+e^{\\theta^{T} x^{(i)}}} ]} \\\\ {=-\\frac{1}{m} \\sum_{i=1}^{m} \\frac{y^{(i)} x_{j}^{(i)}-x_{j}^{(i)} e^{\\theta^{T} x^{(i)}}+y^{(i)} x_{j}^{(i)} e^{\\theta^{T} x^{(i)}}}{1+e^{\\theta^{T} x^{(i)}}}} \\\\ {=-\\frac{1}{m} \\sum_{i=1}^{m} \\frac{y^{(i)}\\left(1+e^{\\theta^{T} x^{(i)}}\\right)-e^{\\theta^{T} x^{(i)}}}{1+e^{\\theta^{T} x^{(i)}}} x_{j}^{(i)}} \\\\ {=-\\frac{1}{m} \\sum_{i=1}^{m}\\left(y^{(i)}-\\frac{e^{\\theta^{T} x^{(i)}}}{1+e^{\\theta^{T} x^{(i)}}}\\right) x_{j}^{(i)}} \\\\ {=-\\frac{1}{m} \\sum_{i=1}^{m}\\left(y^{(i)}-\\frac{1}{1+e^{-\\theta^{T} x^{(i)}} )} x_{j}^{(i)}\\right.} \\\\ {=-\\frac{1}{m} \\sum_{i=1}^{m}\\left[y^{(i)}-h_{\\theta}\\left(x^{(i)}\\right)\\right] x_{j}^{(i)}} \\\\ {=\\frac{1}{m} \\sum_{i=1}^{m}\\left[h_{\\theta}\\left(x^{(i)}\\right)-y^{(i)}\\right] x_{j}^{(i)}}\\end{array}$$即$$\\frac{\\partial J(\\theta)}{\\partial \\theta_{j}}=\\frac{1}{m} \\sum_{i=1}^{m}\\left[h_{\\theta}\\left(x^{(i)}\\right)-y^{(i)}\\right] x_{j}^{(i)}$$","categories":[],"tags":[{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"},{"name":"分类","slug":"分类","permalink":"http://yoursite.com/tags/分类/"},{"name":"逻辑回归","slug":"逻辑回归","permalink":"http://yoursite.com/tags/逻辑回归/"},{"name":"Sigmoid Function","slug":"Sigmoid-Function","permalink":"http://yoursite.com/tags/Sigmoid-Function/"},{"name":"梯度下降法","slug":"梯度下降法","permalink":"http://yoursite.com/tags/梯度下降法/"}]},{"title":"远程工具的使用","slug":"远程工具的使用","date":"2019-05-31T14:46:48.000Z","updated":"2019-05-31T14:47:20.371Z","comments":true,"path":"2019/05/31/远程工具的使用/","link":"","permalink":"http://yoursite.com/2019/05/31/远程工具的使用/","excerpt":"ssh:在同一局域网下,远程连接 安装openssh-server 1sudo apt-get install openssh-server # 安装openssh-server 远程访问","text":"ssh:在同一局域网下,远程连接 安装openssh-server 1sudo apt-get install openssh-server # 安装openssh-server 远程访问 1ifconfig # 查看远程linux系统的IP地址 在本地终端输入如下代码: 1ssh username@IP地址 进行访问 请求访问的时候会要求输入远程用户密码 vnc:在同一局域网下远程共享桌面 linux安装x11vnc 1sudo apt-get install x11vnc # 安装vnc 设置密码 1x11vnc -storepasswd # 设置密码 终端执行以下代码,进行远程共享桌面 1x11vnc -usepw # 启动远程共享桌面 samba:在同一局域网下共享文件 在远程文件目录下新建文件夹,命名为Share,然后右键选中选择”Local Network Share” 在跳出的页面中勾选Share this folder,并勾选”Allow others to creat and delete files in this folder” 在终端输入以下命令,设置密码 1sudo smbpasswd -a username # 设置密码 在Mac中按下command + 跳出如下选框 在框内输入 1smb://远程linux的IP地址 然后输入用户名及密码,进行访问。","categories":[],"tags":[{"name":"ssh","slug":"ssh","permalink":"http://yoursite.com/tags/ssh/"},{"name":"vnc","slug":"vnc","permalink":"http://yoursite.com/tags/vnc/"},{"name":"smb","slug":"smb","permalink":"http://yoursite.com/tags/smb/"},{"name":"远程工具","slug":"远程工具","permalink":"http://yoursite.com/tags/远程工具/"}]},{"title":"git学习笔记","slug":"git学习笔记","date":"2019-05-31T14:45:51.000Z","updated":"2019-08-10T14:53:25.683Z","comments":true,"path":"2019/05/31/git学习笔记/","link":"","permalink":"http://yoursite.com/2019/05/31/git学习笔记/","excerpt":"","text":"Git 的三个工作区域 工作区 暂存区 Git仓库 Git 提交文件常用命令123456789101112git config --list # 查看配置列表git config --global user.email '[email protected]' # 配置邮箱git config --global user.name 'lupanlpb' # 配置用户名git clone https://github.com/lupanlpb/GitLearn.git # 克隆远程仓库到本地git status # 查看状态git init # 把这个目录变成Git可以管理的仓库git add README.md # 文件添加到仓库git add . # 一个点就把当前目录下所有未追踪的文件全部add了git commit -m \"first commit\" # 把文件提交到仓库git remote add origin [email protected]:username/repositoryname.git # 关联远程仓库git push -u origin master # 把本地库的所有内容推送到远程库上git pull origin master # 同步远程的master Git 关联远程仓库 配置 SSH Key 创建远程仓库 登录 GitHub 创建 repository 关联 GitHub 远程仓库 持续更新中…","categories":[],"tags":[{"name":"git","slug":"git","permalink":"http://yoursite.com/tags/git/"}]},{"title":"Mac下利用Hexo搭建个人博客","slug":"Mac下利用Hexo搭建个人博客","date":"2019-05-31T14:44:44.000Z","updated":"2019-07-24T06:47:22.753Z","comments":true,"path":"2019/05/31/Mac下利用Hexo搭建个人博客/","link":"","permalink":"http://yoursite.com/2019/05/31/Mac下利用Hexo搭建个人博客/","excerpt":"1. 安装git2. 安装node.js3. 安装cnpm","text":"1. 安装git2. 安装node.js3. 安装cnpm 1234sudo su # 切换到root用户npm -v # 查看node.js是否安装成功npm install -g cnpm --registry=https://registry.npm.taobao.org #安装镜像cnpm -v # 查看安装cnpm是否成功 4. 安装hexo12cnpm install -g hexo-cli # 安装hexohexo -v # 查看是否安装成功 5. 创建博客12345678910111213mkdir blog # 博客目录cd blog # 切换到blog目录sudo hexo init # 初始化博客hexo s #启动博客,在浏览器输入localhost:4000hexo n \"My first blog\" # 新建一篇博客文章cd source/_posts/ # 切换目录vim My-first-blog.md # 编辑博客cd ../../ # 切换到blog目录hexo clean # 清理hexo g # 生成博客hexo s # 启动# 到目前为止,博客已经搭建完毕# 下面将博客部署到github 6. 部署博客到github1234567891011121314151617# 1. 登录github# 2. 新建仓库# 3. 在repository name输入你的github昵称 + github.io, 如\"lupanlpb.github.io”# 4. 在description输入介绍信息# 5. 点击create# 6. 安装git部署插件cnpm install --save hexo-deployer-git # 安装过程会报出warning,可不予理会# 7. 修改配置文件vim _config.yml ## 8. 在 Deployment处修改type、reporepo和branch如下deploy: type: git repo: https://github.com/lupanlpb/lupanlpb.github.io.git branch: master# 保存退出# 9. 部署到远端hexo d # 部署,并根据提示输入github账号和密码 现在可以访问https://lupanlpb.github.io/了,这样博客就部署到github了! 7. 更换主题 推荐使用Even123456git clone https://github.com/litten/hexo-theme-yilia.git themes/yilia # 下载主题,在blog/themes目录下vim _config.yml # 修改配置文件将theme: landscape改为theme: even,保存退出hexo cleanhexo ghexo shexo d # 部署到远端","categories":[],"tags":[{"name":"git","slug":"git","permalink":"http://yoursite.com/tags/git/"},{"name":"hexo","slug":"hexo","permalink":"http://yoursite.com/tags/hexo/"},{"name":"Mac","slug":"Mac","permalink":"http://yoursite.com/tags/Mac/"},{"name":"github","slug":"github","permalink":"http://yoursite.com/tags/github/"},{"name":"Even","slug":"Even","permalink":"http://yoursite.com/tags/Even/"},{"name":"个人博客","slug":"个人博客","permalink":"http://yoursite.com/tags/个人博客/"}]},{"title":"Hello World","slug":"hello-world","date":"2019-05-26T15:37:19.240Z","updated":"2019-05-26T15:37:19.240Z","comments":true,"path":"2019/05/26/hello-world/","link":"","permalink":"http://yoursite.com/2019/05/26/hello-world/","excerpt":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post","text":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post 1$ hexo new \"My New Post\" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment","categories":[],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"http://yoursite.com/tags/Hexo/"}]}]}