算法学习【第三篇】:链表
链表#
1 2 3 4 5 6 7 | 为什么需要链表 顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以 使用起来并不是很灵活。 链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。 链表的定义 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是 在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址) |
单向链表#
1 2 | 单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一 个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值 |
1 2 3 | ·表元素域elem用来存放具体的数据。 ·链接域next用来存放下一个节点的位置(python中的标识) ·变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。 |
节点实现#
1 2 3 4 5 6 7 | class SingleNode(object): ““单链表的结点" def_init_(self,item): #_item存放数据元素 self.item=item #_next是下一个节点的标识 self.next=None |
单链表的操作(其他链表大致相同)#
1 2 3 4 5 6 7 8 | ·is_empty0链表是否为空 ·length0链表长度 ·travel0遍历整个链表 ·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 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 | # coding:utf-8 class Node(object): """节点Node""" def __init__(self, elem): self.elem = elem self.next = None class SingleLinkList(object): """单链表""" def __init__(self, node=None): self.__head = None def is_empty(self): '''链表是否为空''' return self.__head == None def length(self): '''链表长度''' # cur游标,用来移动遍历节点 cur = self.__head # count记录数量 count = 0 while cur != None: count += 1 cur = cur.next return count def travel(self): '''遍历整个链表''' cur = self.__head while cur != None: print(cur.elem, end=" ") cur = cur.next print('') def add(self, item): '''链表头部添加元素,头插法''' node = Node(item) node.next = self.__head 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 def insert(self, pos, item): '''指定位置添加元素 pos 从0开始 ''' if pos <= 0: self.add(item) elif pos > (self.length() - 1): self.append(item) else: pre = self.__head count = 0 while count < (pos - 1): count += 1 pre = pre.next # 当循环退出后,pre指向pos-1位置 node = Node(item) node.next = pre.next pre.next = node def remove(self, item): '''删除节点''' cur = self.__head pre = None while cur != 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 def search(self, item): '''查找节点是否存在''' cur = self.__head while cur != None: if cur.elem == item: return True else: cur = cur.next return False if __name__ == '__main__': l1 = SingleLinkList() print(l1.is_empty()) print(l1.length()) l1.append(1) print(l1.is_empty()) print(l1.length()) l1.append(2) l1.add(8) l1.append(3) l1.append(4) l1.append(5) l1.append(6) l1.insert(-1, 9) l1.travel() l1.insert(3, 100) l1.travel() l1.insert(10, 200) l1.travel() l1.remove(100) l1.travel() l1.remove(9) l1.travel() l1.remove(200) l1.travel() ''' True 0 False 1 9 8 1 2 3 4 5 6 9 8 1 100 2 3 4 5 6 9 8 1 100 2 3 4 5 6 200 9 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 ''' |
单向循环链表#
单链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为None,而是指向链表的头节点
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 | # coding:utf-8 class Node(object): """结点Node""" def __init__(self, elem): self.elem = elem self.next = None self.prev = None class DoubuleLinkList(object): """单向循环链表""" def __init__(self, node=None): self.__head = node if node: node.next = node def is_empty(self): '''链表是否为空''' return self.__head == None def length(self): '''链表长度''' if self.is_empty(): return 0 # cur游标,用来移动遍历节点 cur = self.__head # count记录数量 count = 1 while cur.next != self.__head: count += 1 cur = cur.next return count def travel(self): '''遍历整个链表''' if self.is_empty(): return cur = self.__head while cur.next != self.__head: print(cur.elem, end=" ") cur = cur.next # 退出循环,cur指向尾结点,但尾结点的元素未打印 print(cur.elem) def add(self, item): '''链表头部添加元素,头插法''' node = Node(item) if self.is_empty(): self.__head = node node.next = node else: cur = self.__head while cur.next != self.__head: cur = cur.next # 退出循环,cur指向尾结点 node.next = self.__head self.__head = node # cur.next = node cur.next = self.__head def append(self, item): '''链表尾部添加元素,尾插法''' node = Node(item) if self.is_empty(): self.__head = node node.next = node else: cur = self.__head while cur.next != self.__head: cur = cur.next # node.next=cur.next node.next = self.__head cur.next = node def insert(self, pos, item): '''指定位置添加元素 pos 从0开始 ''' if pos <= 0: self.add(item) elif pos > (self.length() - 1): self.append(item) else: pre = self.__head count = 0 while count < (pos - 1): count += 1 pre = pre.next # 当前循环退出后,pre指向pos-1位置 node = Node(item) node.next = pre.next pre.next = node def remove(self, item): '''删除节点''' cur = self.__head pre = None while cur.next != self.__head: if cur.elem == item: # 先判断是否是头节点 if cur == self.__head: # 头结点情况 # 找尾结点 rear = self.__head while rear.next != self.__head: rear = rear.next self.__head = cur.next rear.next = self.__head else: # 中间结点 pre.next = cur.next return else: pre = cur cur = cur.next # 退出循环,cur指向尾结点 if cur.elem == item: if cur == self.__head: # 链表只有一个结点 self.__head = None else: # pre.next=cur.next pre.next = self.__head def search(self, item): '''查找节点是否存在''' if self.is_empty(): return False cur = self.__head while cur.next != self.__head: if cur.elem == item: return True else: cur = cur.next # 退出循环,cur指向尾结点 if cur.elem == item: return True return False if __name__ == '__main__': l1 = DoubuleLinkList() print(l1.is_empty()) print(l1.length()) l1.append(1) print(l1.is_empty()) print(l1.length()) l1.append(2) l1.add(8) l1.append(3) l1.append(4) l1.append(5) l1.append(6) l1.insert(-1, 9) l1.travel() l1.insert(3, 100) l1.travel() l1.insert(10, 200) l1.travel() l1.remove(100) l1.travel() l1.remove(9) l1.travel() l1.remove(200) l1.travel() ''' True 0 False 1 9 8 1 2 3 4 5 6 9 8 1 100 2 3 4 5 6 9 8 1 100 2 3 4 5 6 200 9 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 ''' |
双向链表#
一种更复杂的链表是“双向链表”或“双面链表”。每个节点有两个链接:一个指向前一个节点,当此节点为第
一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值
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 | # coding:utf-8 class Node(object): """结点Node""" def __init__(self, elem): self.elem = elem self.next = None self.prev=None class DoubuleLinkList(object): """双链表""" def __init__(self, node=None): self.__head = None def is_empty(self): '''链表是否为空''' return self.__head == None def length(self): '''链表长度''' # cur游标,用来移动遍历节点 cur = self.__head # count记录数量 count = 0 while cur != None: count += 1 cur = cur.next return count def travel(self): '''遍历整个链表''' cur = self.__head while cur != None: print(cur.elem, end=" ") cur = cur.next print('') def add(self, item): '''链表头部添加元素,头插法''' node = Node(item) node.next = self.__head self.__head = node node.next.prev=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 insert(self, pos, item): '''指定位置添加元素 pos 从0开始 ''' if pos <= 0: self.add(item) elif pos > (self.length() - 1): self.append(item) else: cur=self.__head count = 0 while count < pos: count += 1 cur = cur.next # 当循环退出后,cur指向pos位置 node = Node(item) node.next=cur node.prev=cur.prev cur.prev.next=node cur.prev=node def remove(self, item): '''删除节点''' cur = self.__head while cur != None: if cur.elem == item: # 先判断是否是头节点 # 头节点 if cur == self.__head: if cur.next: #判断链表是否只有一个结点 cur.next.prev=None else: cur.prev.next=cur.next if cur.next: cur.next.prev = cur.prev break else: cur = cur.next def search(self, item): '''查找节点是否存在''' cur = self.__head while cur != None: if cur.elem == item: return True else: cur = cur.next return False if __name__ == '__main__': l1 = DoubuleLinkList() print(l1.is_empty()) print(l1.length()) l1.append(1) print(l1.is_empty()) print(l1.length()) l1.append(2) l1.add(8) l1.append(3) l1.append(4) l1.append(5) l1.append(6) l1.insert(-1, 9) l1.travel() l1.insert(3, 100) l1.travel() l1.insert(10, 200) l1.travel() l1.remove(100) l1.travel() l1.remove(9) l1.travel() l1.remove(200) l1.travel() ''' True 0 False 1 9 8 1 2 3 4 5 6 9 8 1 100 2 3 4 5 6 9 8 1 100 2 3 4 5 6 200 9 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 ''' |
双向循环链表#
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 | # 链表的节点 class Node(object): def __init__(self, item): self.item = item # 节点数值 self.prev = None # 用于指向前一个元素 self.next = None # 用于指向后一个元素 # 双向循环链表 class DoubleCircleLinkList(object): def __init__(self): self.__head = None # 初始化的时候头节点设为空、 # 判断链表是否为空,head为None 的话则链表是空的 def is_empty(self): return self.__head is None # 头部添加元素的方法 def add(self, item): node = Node(item) # 新建一个节点node 里面的值是item # 如果链表是空的,则node的next和prev都指向自己(因为是双向循环),head指向node if self.is_empty(): self.__head = node node.next = node node.prev = node # 否则链表不空 else: node.next = self.__head # node的next设为现在的head node.prev = self.__head.prev # node的prev 设为现在head的prev self.__head.prev.next = node # 现在head的前一个元素的next设为node self.__head.prev = node # 现在head的前驱 改为node self.__head = node # 更改头部指针 # 尾部添加元素方法 def append(self, item): # 如果当前链表是空的 那就调用头部插入方法 if self.is_empty(): self.add(item) # 否则链表不为空 else: node = Node(item) # 新建一个节点node # 因为是双向循环链表,所以head的prev其实就是链表的尾部 node.next = self.__head # node的下一个设为头 node.prev = self.__head.prev # node的前驱设为现在头部的前驱 self.__head.prev.next = node # 头部前驱的后继设为node self.__head.prev = node # 头部自己的前驱改为node # 获得链表长度 节点个数 def length(self): # 如果链表是空的 就返回0 if self.is_empty(): return 0 # 如果不是空的 else: cur = self.__head # 临时变量cur表示当前位置 初始化设为头head count = 1 # 设一个计数器count,cur每指向一个节点,count就自增1 目前cur指向头,所以count初始化为1 # 如果cur.next不是head,说明cur目前不是最后一个元素,那么count就1,再让cur后移一位 while cur.next is not self.__head: count += 1 cur = cur.next # 跳出循环说明所有元素都被累加了一次 返回count就是一共有多少个元素 return count # 遍历链表的功能 def travel(self): # 如果当前自己是空的,那就不遍历 if self.is_empty(): return # 链表不空 else: cur = self.__head # 临时变量cur表示当前位置,初始化为链表的头部 # 只要cur的后继不是头说明cur不是最后一个节点,我们就输出当前值,并让cur后移一个节点 while cur.next is not self.__head: print(cur.item, end=" ") cur = cur.next # 当cur的后继是head的时候跳出循环了,最后一个节点还没有打印值 在这里打印出来 print(cur.item) # 置顶位置插入节点 def insert(self, pos, item): # 如果位置<=0 则调用头部插入方法 if pos <= 0: self.add(item) # 如果位置是最后一个或者更大 就调用尾部插入方法 elif pos > self.length() - 1: self.append(item) # 否则插入位置就是链表中间 else: index = 0 # 设置计数器,用于标记我们后移了多少步 cur = self.__head # cur标记当前所在位置 # 让index每次自增1 ,cur后移,当index=pos-1的时候说明cur在要插入位置的前一个元素,这时候停下 while index < pos - 1: index += 1 cur = cur.next # 跳出循环,cur在要插入位置的前一个元素,将node插入到cur的后面 node = Node(item) # 新建一个节点 node.next = cur.next # node的后继设为cur的后继 node.prev = cur # node的前驱设为cur cur.next.prev = node # cur后继的前驱改为node cur.next = node # cur后继改为node # 删除节点操作 def remove(self, item): # 如果链表为空 直接不操作 if self.is_empty(): return # 链表不为空 else: cur = self.__head # 临时变量标记位置,从头开始 # 如果头结点就是 要删除的元素 if cur.item == item: # 如果只有一个节点 链表就空了 head设为None if self.length() == 1: self.__head = None # 如果多个元素 else: self.__head = cur.next # 头指针指向cur的下一个 cur.next.prev = cur.prev # cur后继的前驱改为cur的前驱 cur.prev.next = cur.next # cur前驱的后继改为cur的后继 # 否则 头节点不是要删除的节点 我们要向下遍历 else: cur = cur.next # 把cur后移一个节点 # 循环让cur后移一直到链表尾元素位置,期间如果找得到就删除节点,找不到就跳出循环, while cur is not self.__head: # 找到了元素cur就是要删除的 if cur.item == item: cur.prev.next = cur.next # cur的前驱的后继改为cur的后继 cur.next.prev = cur.prev # cur的后继的前驱改为cur的前驱 cur = cur.next # 搜索节点是否存在 def search(self, item): # 如果链表是空的一定不存在 if self.is_empty(): return False # 否则链表不空 else: cur = self.__head # 设置临时cur从头开始 # cur不断后移,一直到尾节点为止 while cur.next is not self.__head: # 如果期间找到了就返回一个True 结束运行 if cur.item == item: return True cur = cur.next # 从循环跳出来cur就指向了尾元素 看一下为元素是不是要找的 是就返回True if cur.item == item: return True # 所有元素都不是 就返回False 没找到 return False |
链表与顺序表的对比#
链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的
使用要相对灵活。
链表与顺序表的各种操作复杂度如下所示:
1 2 | 注意虽然表面看起来复杂度都是O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前 后移位操作,只能通过拷贝和覆盖的方法进行。 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架