链表 前言 前面讲了顺序表,顺序表的缺点在于无法动态扩容,若顺序表的容量达到上限,再来一个数据,就需要重新申请足够大的内存空间来重新存储。那有没有数据结构能够实现数据来一个就增加一个而不需要重新申请内存空间呢?链表就是这么一个数据结构。
为什么需要链表 顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以使用起来并不是很灵活。
链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
链表的定义 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)。
案例 假设一开是有一个数据200,存储在某个内存空间中,这时候又来一个数据400,也存储在某个内存空间中,600同理。现在先要把这些离散存储的数据给连续起来,怎么实现呢?
首先,把400的内存地址放在200的内存空间里,使得200可以链接到400,同理400->600,这样就可以把一组离散内存空间存储的数据给联系起来,这就是链表。
链表(单向)的示意如下图:
单向链表 单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。
表元素域elem用来存放具体的数据。
链接域next用来存放下一个节点的位置(python中的标识)
变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。
拓展 如何理解Python中的:
1 2 3 a = 10 b = 20 a, b = b, a
由此可见,a, b = b, a,赋值并不是真正的赋值,而是把a, b引用的地址导向给转换。
而pyton之所以能够实现隐式申明是因为变量a保存的是一个地址,a的地址指向谁,a就是谁。
也正因为python的“=”的特殊性,python实现的链表中,链接域并非是地址,而是一个类节点,具体参考下面的节点实现代码。
节点实现 1 2 3 4 5 6 7 class SingleNode (object ): """单链表的结点""" def __init__ (self,item ): self.item = item self.next = None
单链表的操作
is_empty() 链表是否为空
length() 链表长度
travel() 遍历整个链表
add(item) 链表头部添加元素
append(item) 链表尾部添加元素
insert(pos, item) 指定位置添加元素
remove(item) 删除节点(如果有多个相同的,仅删除靠左边的第一个节点)
search(item) 查找节点是否存在
单链表的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 class Node (object ): """节点""" def __init__ (self, elem ): self.elem = elem self.next = None class SingalLinkList (object ): """单链表""" def __init__ (self, node=None ): """创建头节点,指向第一个节点""" self._head = node def is_empty (self ): """链表是否为空""" pass def length (self ): """返回链表长度""" pass cur = self._head count = 0 while cur != None : count += 1 cur = cur.next return count def travel (self ): """遍历链表""" cur = self._head while cur != None : print cur.item, cur = cur.next print "" def add (self, item ): """链表头部添加元素""" pass def append (self, item ): """链表尾部添加元素""" pass def insert (self, item ): """指定位置插入元素""" pass def remove (self, item ): """删除节点""" pass def search (self, item ): """查找节点是否存在""" pass
头部添加元素
1 2 3 4 5 6 7 def add (self, item ): """链表头部添加元素,头插法""" node = Node(item) node.next = self.__head self.__head = node
尾部添加元素
1 2 3 4 5 6 7 8 9 10 11 12 def append (self, item ): """尾部添加元素""" node = SingleNode(item) if self.is_empty(): self._head = node else : cur = self._head while cur.next != None : cur = cur.next cur.next = node
指定位置添加元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def insert (self, pos, item ): """指定位置插入元素 :param pos start from 0 """ pre = self.__head count = 0 if pos < 0 : self.add(item) elif pos > (self.length() - 1 ): self.append(item) else : while count < (pos - 1 ): pre = pre.next count += 1 node = Node(item) node.next = pre.next pre.next = node
删除节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 def remove (self, item ): """删除节点""" cur = self.__head pre = None while cur is not None : if cur.elem == item: if cur == self.__head: self.__head = cur.next else : pre.next = cur.next break else : pre = cur cur = cur.next
查找节点是否存在
1 2 3 4 5 6 7 8 9 def search (self, item ): """查找节点是否存在,返回True or False""" cur = self.__head while cur != None : if cur.elem == item: return True else : cur = cur.next return False
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 if __name__ == "__main__" : ll = SingleLinkList() ll.add(1 ) ll.add(2 ) ll.append(3 ) ll.insert(2 , 4 ) print "length:" ,ll.length() ll.travel() print ll.search(3 ) print ll.search(5 ) ll.remove(1 ) print "length:" ,ll.length() ll.travel()
链表与顺序表的对比 链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的使用要相对灵活。
链表与顺序表的各种操作复杂度如下所示:
操作
链表
顺序表
访问元素
O(n)
O(1)
在头部插入/删除
O(1)
O(n)
在尾部插入/删除
O(n)
O(1)
在中间插入/删除
O(n)
O(n)
注意虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。
补充概念: 当前节点的后一个节点叫做后继节点,前一个节点叫前驱节点。
单向循环链表 单链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为None,而是指向链表的头节点。
操作
is_empty() 判断链表是否为空
length() 返回链表的长度
travel() 遍历
add(item) 在头部添加一个节点
append(item) 在尾部添加一个节点
insert(pos, item) 在指定位置pos添加节点
remove(item) 删除一个节点
search(item) 查找节点是否存在
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 from SingalLinkList import SingalLinkListclass Node (object ): """ 单节点 本链表头节点索引为0 """ def __init__ (self, item ): self.item = item self.next = None class CirSingalLinkList (SingalLinkList ): """ 单向循环链表 """ def __init__ (self, node=None ): """当链表是空,头节点指向None""" super (CirSingalLinkList, self).__init__(node) self.__head = None def is_empty (self ): """和单链表一样,方法不需要修改直接继承""" pass def length (self ): """ 因为尾部节点指针指向头部所以需要修改 """ if self.__head: num = 0 length = self.__head.next while (length != self.__head): num += 1 length = length.next return num+1 return 0 def travel (self ): if self.__head: num = 0 length = self.__head.next print (f"{num} : {self.__head.item} " ) while (length != self.__head): num += 1 print (f"{num} : {length.item} " ) length = length.next else : print ("The SingalLinkList Is None" ) def add (self, item ): """链表头部添加元素,尾部指针指向头节点""" new = Node(item) if not self.__head: self.__head = new new.next = self.__head else : point = self.__head.next while (point.next != self.__head): point = point.next new.next = self.__head point.next = new self.__head = new def append (self, item ): """链表尾部添加元素""" new = Node(item) if not self.__head: self.__head = new new.next = self.__head else : point = self.__head.next while (point.next != self.__head): point = point.next new.next = point.next point.next = new def insert (self, pos, item ): if pos == 0 : self.add(item) return 0 if pos < 0 : """输入负数位置,默认从头插入""" self.add(item) return 0 """指定位置插入元素""" if self.length() < pos: if self.length == 0 : print ("SingalLinkList is Null" ) else : self.append(item) else : new = Node(item) point = self.__head pos -= 1 while pos: pos -= 1 point = point.next new.next = point.next point.next = new def remove (self, item ): """删除指定节点的数据,如果有多个相同的删除左边第一个""" find = False if not self.__head: print ("SingalLinkList is Null!" ) else : pre_point = None point = self.__head while (point.next != self.__head): if point.item == item: find = True break pre_point = point point = point.next if find: if pre_point == None : self.__head = self.__head.next else : pre_point.next = point.next else : print ("warning:No Found The Item That You Want To Delete!" ) if __name__ == "__main__" : ll = SingalLinkList() ll.append(1 ) ll.append(2 ) ll.add(8 ) ll.append(3 ) ll.append(4 ) ll.travel() ll.insert(-1 , 9 ) ll.travel() ll.insert(2 , 100 ) ll.travel() ll.insert(10 , 200 )
3-12视频最后10mins关于链表补充
双向链表 一种更复杂的链表是“双向链表”或“双面链表”。每个节点有两个链接:一个指向前一个节点,当此节点为第一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值。
操作
is_empty() 链表是否为空
length() 链表长度
travel() 遍历链表
add(item) 链表头部添加
append(item) 链表尾部添加
insert(pos, item) 指定位置添加
remove(item) 删除节点
search(item) 查找节点是否存在
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 class Node (object ): """双向链表节点""" def __init__ (self, item ): self.item = item self.next = None self.prev = None class DLinkList (object ): """双向链表""" def __init__ (self ): self._head = None def is_empty (self ): """判断链表是否为空""" return self._head == None def length (self ): """返回链表的长度""" cur = self._head count = 0 while cur != None : count += 1 cur = cur.next return count def travel (self ): """遍历链表""" cur = self._head while cur != None : print cur.item, cur = cur.next print "" def add (self, item ): """头部插入元素""" node = Node(item) if self.is_empty(): self._head = node else : node.next = self._head self._head.prev = node self._head = node def append (self, item ): """尾部插入元素""" node = Node(item) if self.is_empty(): self._head = node else : cur = self._head while cur.next != None : cur = cur.next cur.next = node node.prev = cur def search (self, item ): """查找元素是否存在""" cur = self._head while cur != None : if cur.item == item: return True cur = cur.next return False
指定位置插入节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def insert (self, pos, item ): """在指定位置添加节点""" if pos <= 0 : self.add(item) elif pos > (self.length()-1 ): self.append(item) else : node = Node(item) cur = self._head count = 0 while count < (pos-1 ): count += 1 cur = cur.next node.prev = cur node.next = cur.next cur.next .prev = node cur.next = node
删除元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def remove (self, item ): """删除节点""" cur = self.__head while cur is not None : if cur.elem == item: if cur == self.__head: self.__head = cur.next if cur.next : cur.next .prev = None else : cur.prev.next = cur.next if cur.next : cur.next .prev = cur.prev return else : cur = cur.next
测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 if __name__ == "__main__" : ll = DoubleLinkList() ll.append(1 ) ll.append(2 ) ll.add(8 ) ll.append(3 ) ll.append(4 ) ll.insert(-1 , 9 ) ll.insert(2 , 100 ) ll.insert(10 , 200 ) ll.travel() ll.remove(9 ) ll.travel() ll.remove(200 ) ll.travel() ll.remove(400 ) ll.travel()
结果