链表定义
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
单向链表,又叫单链表、单向表,是由一个个节点组成的,每个节点是一种信息集合,包含元素本身以及下一个节点的地址。节点在内存中是非连续分布的,在程序运行期间,根据需要可以动态的创建节点,这就使得链表的长度没有逻辑上的限制,有限制的是堆的大小。
在单向链表中,每个节点中存储着下一个节点的地址。基于单向链表,在每个节点内增加一个前驱节点的地址,这就是所谓的双向链表,又叫双链表、双向表。
如单向链表的尾节点指针指向头节点,即为循环单向链表。
如单向链表的头、尾节点指针互相指,即为循环双向链表。
指针是C/C++等语言中的概念,也就是内存地址;python中没有指针,也很少直接操作地址。
在 python 这一类没有指针的语言里,很难真正体现出链表的精髓,只能模拟出一个静态链表来意思意思。
单向表类的实现
模仿python中二叉树的节点Node类,把左右子树的left,right换成一个Next,就有点像了。因为刚学python不久功力有限,愣是用字符串和列表,伪实现了一个单向表的类:
class Node(): def __init__(self,val=None,Next=None): self.val = val self.Next = Next def __repr__(self): return 'listNode('+str(self.val)+')' def __getitem__(self, slices): list = self.values() return list[slices] def is_empty(self): return self.val is None def value(self): return self.val def values(self): head = self t = 'head' r = [] while eval(t) is not None: r.append(eval(t).val) t+='.Next' return r def size(self): if self.is_empty(): return 0 r = self.values() return len(r) def append(self, node): if self.val is None: self.val = node return head = self t = 'head' while eval(t) is not None: t+='.Next' exec(f'{t}=Node({node})') def pop(self): head = self if head.size() < 2: res = head.val head.val = None return res t = 'head' while eval(t).Next is not None: t+='.Next' res = eval(t).val exec(f'{t}=None') return res def pprint(self): r = self.values() r = [i.join(["'"]*2) if type(i) is str else str(i) for i in r] print('->'.join(r))
之所以说是“伪实现”,因为根本就是用字符串和列表模拟出来的效果。尽管这个类如此不入门,有点傻傻的,用上海咸话的讲法,称之为“捣浆糊”。但还是能完成不少功能的。呈上测试代码,如下:
1. 逐个赋值
>>> LinkedList = Node() >>> LinkedList.is_empty() True >>> LinkedList.size() 0 >>> LinkedList = Node(1) >>> LinkedList.is_empty() False >>> LinkedList.size() 1 >>> LL = LinkedList >>> LL.Next = Node(2) >>> LL listNode(1) >>> LL.Next listNode(2) >>> LL.val 1 >>> LL.Next.val 2 >>> LL.value() 1 >>> LL.Next.value() 2 >>> LL.values() [1, 2] >>> >>> >>> LinkedList = Node() >>> LinkedList = Node(1,Node(2)) >>> linked = Node(1,Node(2)) >>> linked.Next.Next = Node('3',Node(4)) >>> linked.pprint() 1->2->'3'->4 >>> linked.values() [1, 2, '3', 4] >>>
注:此链表类允许数字、字符串混装
2.追加append()和弹出pop()
>>> single = Node(1,Node(2,Node(3,Node(4,Node(5))))) >>> single.pprint() 1->2->3->4->5 >>> single.append(6) >>> single.pprint() 1->2->3->4->5->6 >>> single.pop() 6 >>> single.pop() 5 >>> single.pprint() 1->2->3->4 >>>
3. 嵌套赋值或用循环赋值
>>> single = Node(1,Node(2,Node(3,Node(4,Node(5))))) >>> single.size() 5 >>> single.values() [1, 2, 3, 4, 5] >>> >>> >>> link = Node() >>> for i in range(1,9): link.append(i) >>> link.pprint() 1->2->3->4->5->6->7->8 >>>
4. 索引和切片
>>> lList = Node() >>> t = [lList.append(i) for i in range(1,9)] >>> lList.pprint() 1->2->3->4->5->6->7->8 >>> lList[:] [1, 2, 3, 4, 5, 6, 7, 8] >>> lList[::-1] [8, 7, 6, 5, 4, 3, 2, 1] >>> lList[0] 1 >>> lList[1] 2 >>> lList[7] 8 >>> lList[-1] 8 >>> lList[-2] 7 >>> lList[-8] 1 >>> lList[:5] [1, 2, 3, 4, 5] >>> lList[5:] [6, 7, 8] >>> lList[::2] [1, 3, 5, 7] >>> lList[1::2] [2, 4, 6, 8] >>>
注:链表一般是不带索引功能的,这里用类的内置方法__getitem__(self, slices)来强加一个索引和切片的功能,完全是继承了列表的功能。
5. 删除和插入
>>> single = Node(1,Node(2,Node(3,Node(4,Node(5))))) >>> single.pprint() 1->2->3->4->5 >>> single.Next.Next = single.Next.Next.Next >>> single.pprint() 1->2->4->5 >>> >>> tmp = single.Next.Next >>> single.Next.Next = Node(6) >>> single.Next.Next.Next = tmp >>> single.pprint() 1->2->6->4->5 >>>
注:删除和插入方法没有直接写进这个类里,但也能用代码实现类似操作。
单链表真实的删除和插入操作过程,如下所示:
6. 单链表的反转(倒序)
>>> linked = Node() >>> assign = [linked.append(i) for i in range(1,9)] >>> linked.pprint() 1->2->3->4->5->6->7->8 >>> tmp,linked = linked,Node() >>> while not tmp.is_empty(): linked.append(tmp.pop()) >>> linked.pprint() 8->7->6->5->4->3->2->1 >>>
注:用循环反复追加append原链表的pop()返回值完成倒序操作。
“捣浆糊”版的链表类就到此结束吧,准备下一篇再来重写一个进阶一点的链表类 ^_^