链表及其算法

1. 链表

2. 单向链表 

3. 双向链表

4. 单向循环链表

 

 

1. 链表

为什么需要链表

顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以使用起来并不是很灵活。

链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。

链表的定义

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)。

链表与顺序表的对比

链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的使用要相对灵活。

链表与顺序表的各种操作复杂度如下所示:

操作链表顺序表
访问元素 O(n) O(1)
在头部插入/删除 O(1) O(n)
在尾部插入/删除 O(1) O(1)
在中间插入/删除 O(1) O(n)

注意虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作:

  • 链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是 O(1)。
  • 顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。

  

2. 单向链表

单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。

  • 元素域 elem 用来存放具体的数据。
  • 链接域 next 用来存放下一个节点的位置(python 中的 id 标识)。
  • 变量 p 指向链表的头节点(首节点)的位置,从 p 出发能找到表中的任意节点。

代码实现

自定义单链表的操作

  • is_empty():链表是否为空。
  • length():链表长度。
  • travel():遍历整个链表。
  • add(item):链表头部添加元素。
  • append(item):链表尾部添加元素。
  • insert(pos, item):指定位置添加元素。
  • remove(item):删除元素。
  • search(item):元素是否存在。
  • reverse():反转链表。

头部增加节点:

指定位置添加节点:

 删除节点:

 实现:

  1 class Node:
  2     """单链表的节点"""
  3     def __init__(self, item):
  4         # item 存放数据元素
  5         self.item = item
  6         # next是下一个节点的标识
  7         # 直接指向下一个节点对象即可
  8         self.next = None
  9         
 10 
 11 class SingleLinkList:
 12     """单链表"""
 13     def __init__(self, node=None):
 14         # head只是一个变量,指向头节点
 15         self.head = node
 16         
 17     def is_empty(self):
 18         """判断链表是否为空"""
 19         return self.head == None
 20         
 21     def length(self):
 22         """链表长度"""
 23         # cur代表当前所在节点,先初始化指向头节点
 24         cur = self.head
 25         count = 0
 26         #尾节点指向None,当未到达尾节点时
 27         while cur != None:
 28             count += 1
 29             # 将cur后移一个节点
 30             cur = cur.next
 31         return count
 32         
 33     def travel(self):
 34         """遍历链表"""
 35         # cur初始化指向头节点
 36         cur = self.head
 37         while cur != None:
 38             print(cur.item, end=" ")
 39             cur = cur.next   
 40         print()
 41     
 42     def append(self, item):
 43         """尾部添加节点,即尾插法"""
 44         node = Node(item)
 45         # 先判断链表是否为空,若是空链表,则将head指向新节点
 46         if self.is_empty():
 47             self.head = node
 48         # 若不为空,则找到尾部,将尾部节点的next指向新节点
 49         else:
 50             cur = self.head
 51             while cur.next != None:
 52                 cur = cur.next
 53             cur.next = node
 54     
 55     def add(self, item):
 56         """头部添加节点,即头插法"""
 57         # 先创建一个保存item值的节点
 58         node = Node(item)
 59         # 该节点先指向头节点
 60         node.next = self.head
 61         # 头节点再指向该节点
 62         self.head = node
 63         
 64     def insert(self, pos, item):
 65         """指定位置添加节点"""
 66         # 若指定位置为第一个元素之前,则执行头插法
 67         if pos <= 0:
 68             self.add(item)
 69         # 若指定位置超过链表尾部,则执行尾插法
 70         elif pos > (self.length()-1):
 71             self.append(item)
 72         # 找到指定位置
 73         else:
 74             node = Node(item)
 75             # pre用来指向指定位置pos的前一个位置pos-1,初始从头节点开始移动到指定位置
 76             pre = self.head
 77             for i in range(pos-1):
 78                 pre = pre.next
 79             # 先将新节点的next指向插入指定位置本身的节点
 80             node.next = pre.next
 81             # 将插入指定位置的前一个节点的next指向新节点
 82             pre.next = node
 83             
 84     def search(self, item):
 85         """查看节点是否存在,返回布尔值"""
 86         cur = self.head
 87         while cur != None:
 88             if cur.item == item:
 89                 return True
 90             cur = cur.next
 91         return False
 92         
 93     def remove(self, item):
 94         """删除节点"""
 95         cur = self.head
 96         pre = None
 97         while cur != None:
 98             # 找到了指定元素
 99             if cur.item == item:
100                 # 如果头节点就是被删除的节点
101                 if not pre:
102                     # 将头指针指向头结点的后一个节点
103                     self.head = cur.next
104                 else:
105                     # 将删除位置前一个节点的next指向删除位置的后一个节点
106                     pre.next = cur.next
107                 break
108             else:
109                 # 继续按链表后移节点
110                 pre = cur
111                 cur = cur.next
112 
113     def reverse(self):
114         if self.head is None:
115             print("链表为空,无需反转")
116             return
117         pre = None
118         while self.head != None:
119             tmp = pre
120             pre = self.head
121             self.head = self.head.next
122             pre.next = tmp
123         self.head = pre
124         print("反转结果:", end="")
125         self.travel()
126     
127     
128 if __name__ == "__main__":
129     ll = SingleLinkList()
130     print(ll.is_empty())  # True
131     ll.append(1)
132     ll.append(2)
133     ll.append(4)
134     print(ll.is_empty())  # False
135     ll.travel()  # 1 2 4
136     ll.add(5) 
137     ll.travel()  # 5 1 2 4
138     
139     ll.insert(-1, -1)
140     ll.travel()  # -1 5 1 2 4
141     ll.insert(9, 10)
142     ll.travel()  # -1 5 1 2 4 10
143     ll.insert(2, 11)
144     ll.travel()  # -1 5 11 1 2 4 10
145     
146     print(ll.search(11))  # True
147     print(ll.search(100))  # False
148     ll.remove(-1)
149     ll.remove(1)
150     ll.travel()  # 5 11 2 4 10
151     ll.reverse()  # 反转结果:10 4 2 11 5

 

3. 双向链表

一种更复杂的链表是“双向链表”或“双面链表”。每个节点有两个链接:

  • 一个指向前一个节点,当此节点为第一个节点时,指向空值;
  • 而另一个指向下一个节点,当此节点为最后一个节点时,指向空值。

指定位置插入节点:

删除元素:

实现:

  1 from test import SingleLinkList
  2 
  3 
  4 class Node:
  5     "双向链表节点"
  6     def __init__(self, item):
  7         self.item = item
  8         self.prev = None
  9         self.next = None
 10 
 11 
 12 class DoubleLinkList(SingleLinkList):
 13     "双向链表"
 14     
 15     """以下方法继承自SingleLinkList"""
 16     # def __init__(self, node=None):
 17         # # head只是一个变量,指向头节点
 18         # self.head = node
 19     
 20     # def is_empty(self):
 21         # "判断链表是否为空"
 22         # return self.head == None
 23         
 24     # def length(self):
 25         # "链表长度"
 26         # # cur代表当前所在节点,先初始化指向头节点
 27         # cur = self.head
 28         # count = 0
 29         # #尾节点指向None,当未到达尾节点时
 30         # while cur != None:
 31             # count += 1
 32             # # 将cur后移一个节点
 33             # cur = cur.next
 34         # return count
 35         
 36     # def travel(self):
 37         # "遍历链表"
 38         # # cur初始化指向头节点
 39         # cur = self.head
 40         # while cur != None:
 41             # print(cur.item, end=" ")
 42             # cur = cur.next   
 43         # print()
 44         
 45     # def search(self, item):
 46         # "查看节点是否存在,返回布尔值"
 47         # cur = self.head
 48         # while cur != None:
 49             # if cur.item == item:
 50                 # return True
 51             # cur = cur.next
 52         # return False
 53 
 54     def append(self, item):
 55         "尾部添加节点,即尾插法"
 56         node = Node(item)
 57         # 先判断链表是否为空,若是空链表,则将head指向新节点
 58         if self.is_empty():
 59             self.head = node
 60         # 若不为空,则找到尾部,将尾部节点的next指向新节点
 61         else:
 62             cur = self.head
 63             while cur.next != None:
 64                 cur = cur.next
 65             cur.next = node
 66             node.prev = cur
 67     
 68     def add(self, item):
 69         "头部添加节点,即头插法"
 70         # 先创建一个保存item值的节点
 71         node = Node(item)
 72         # 如果是空链表,将head指向node
 73         if self.is_empty():
 74             self.head = node
 75         else:
 76             # 将node的next指向头节点
 77             node.next = self.head
 78             # 将头节点的prev指向node
 79             self.head.prev = node
 80             # 将头节点指向node
 81             self.head = node
 82         
 83     def insert(self, pos, item):
 84         "指定位置添加节点"
 85         # 若指定位置为第一个元素之前,则执行头插法
 86         if pos <= 0:
 87             self.add(item)
 88         # 若指定位置超过链表尾部,则执行尾插法
 89         elif pos > (self.length()-1):
 90             self.append(item)
 91         # 找到指定位置
 92         else:
 93             node = Node(item)
 94             cur = self.head
 95             # 移动到指定位置的前一个位置
 96             for i in range(pos-1):
 97                 cur = cur.next
 98             # 将node的prev指向cur
 99             node.prev = cur
100             # 将node的next指向指定位置
101             node.next = cur.next
102             # 将指定位置的prev指向node
103             cur.next.prev = node
104             # 将cur的next指向node
105             cur.next = node
106         
107     def remove(self, item):
108         "删除节点"
109         if self.is_empty():
110             return 
111         else:
112             cur = self.head
113             # 如果头节点就是被删除的节点
114             if cur.item == item:
115                 # 如果链表只有这一个节点
116                 if cur.next == None:
117                     self.head = None
118                 else:
119                     # 将第二个节点的prev指向None
120                     cur.next.prev = None
121                     # 将head指向第二个节点
122                     self.head = cur.next
123                 return 
124             while cur != None:
125                 # 找到了指定元素
126                 if cur.item == item:
127                     # 将cur的前一个节点的next指向cur的下一个节点
128                     cur.prev.next = cur.next
129                     # 将cur的下一个节点的next指向cur的前一个节点
130                     cur.next.prev = cur.prev
131                     break
132                 else:
133                     # 继续按链表后移节点
134                     cur = cur.next
135                     
136                     
137 # 测试
138 if __name__ == "__main__":
139     dl = DoubleLinkList()
140     dl.add(1)
141     dl.add(2)
142     dl.append(3)
143     dl.insert(2, 4)
144     dl.insert(4, 5)
145     dl.insert(0, 6)
146     print("length: ", dl.length())
147     dl.travel()
148     print(dl.search(3))
149     print(dl.search(13))
150     dl.remove(6)
151     dl.travel()

 

4. 单向循环链表

单链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为None,而是指向链表的头节点。

操作:

  • is_empty():判断链表是否为空
  • length():返回链表的长度
  • travel():遍历
  • add(item):在头部添加一个节点
  • append(item):在尾部添加一个节点
  • insert(pos, item):在指定位置pos添加节点
  • remove(item):删除一个节点
  • search(item):查找节点是否存在

实现:

  1 class Node:
  2     """单链表的节点"""
  3     def __init__(self, item):
  4         # item 存放数据元素
  5         self.item = item
  6         # next是下一个节点的标识
  7         # 直接指向下一个节点对象即可
  8         self.next = None
  9         
 10 
 11 class SingleCycLinkList:
 12     """单向循环链表"""
 13     def __init__(self, node=None):
 14         # head只是一个变量,指向头节点
 15         self.head = node
 16         
 17     def is_empty(self):
 18         """判断链表是否为空"""
 19         return self.head == None
 20         
 21     def length(self):
 22         """链表长度"""
 23         if self.is_empty():
 24             return 0
 25         # cur代表当前所在节点,先初始化指向头节点
 26         cur = self.head
 27         count = 1
 28         #尾节点指向头节点,当未到达尾节点时
 29         while cur.next != self.head:
 30             count += 1
 31             # 将cur后移一个节点
 32             cur = cur.next
 33         return count
 34         
 35     def travel(self):
 36         """遍历链表"""
 37         if self.is_empty():
 38             return 
 39         # cur初始化指向头节点
 40         cur = self.head
 41         while cur.next != self.head:
 42             print(cur.item, end=" ")
 43             cur = cur.next   
 44         print(cur.item, end="\n")
 45     
 46     def append(self, item):
 47         """尾部添加节点,即尾插法"""
 48         node = Node(item)
 49         # 先判断链表是否为空,若是空链表,则将head指向新节点
 50         if self.is_empty():
 51             self.head = node
 52             node.next = node
 53         # 若不为空,则找到尾部,将尾部节点的next指向新节点
 54         else:
 55             cur = self.head
 56             print(1)
 57             while cur.next != self.head:
 58                 cur = cur.next
 59             # 将尾节点指向node
 60             cur.next = node
 61             # 将node指向头节点
 62             node.next = self.head
 63     
 64     def add(self, item):
 65         """头部添加节点,即头插法"""
 66         # 先创建一个保存item值的节点
 67         node = Node(item)
 68         if self.is_empty():
 69             self.head = node
 70             node.next = node
 71         else:
 72             # 该节点先指向头节点
 73             node.next = self.head
 74             # 移动到链表尾部,将尾部节点的next指向node
 75             cur = self.head
 76             while cur.next != self.head:
 77                 cur = cur.next
 78             cur.next = node
 79             # 头节点再指向该节点
 80             self.head = node
 81             
 82         
 83     def insert(self, pos, item):
 84         """指定位置添加节点"""
 85         # 若指定位置为第一个元素之前,则执行头插法
 86         if pos <= 0:
 87             self.add(item)
 88         # 若指定位置超过链表尾部,则执行尾插法
 89         elif pos > (self.length()-1):
 90             self.append(item)
 91         # 找到指定位置
 92         else:
 93             node = Node(item)
 94             # pre用来指向指定位置pos的前一个位置pos-1,初始从头节点开始移动到指定位置
 95             pre = self.head
 96             for i in range(pos-1):
 97                 pre = pre.next
 98             # 先将新节点的next指向插入指定位置本身的节点
 99             node.next = pre.next
100             # 将插入指定位置的前一个节点的next指向新节点
101             pre.next = node
102             
103     def search(self, item):
104         """查看节点是否存在,返回布尔值"""
105         if self.is_empty():
106             return False
107         cur = self.head
108         while cur.next != self.head:
109             if cur.item == item:
110                 return True
111             cur = cur.next
112             # cur指向尾节点
113         if cur.item == item:
114             return True
115         return False
116         
117     def remove(self, item):
118         """删除节点"""
119         # 若链表为空,则直接返回
120         if self.is_empty():
121             return 
122         cur = self.head
123         pre = None
124         # 若头节点就是要被删除的节点
125         if cur.item == item:
126             # 如果链表不止一个节点
127             if cur.next != self.head:
128                 # 先找到尾部节点,将尾部节点的next指向第二个节点
129                 while cur.next != self.head:
130                     cur = cur.next
131                 cur.next = self.head.next
132                 self.head = self.head.next
133             # 如果链表只有一个节点
134             else:
135                 self.head = None
136         # 若头节点不是要被删除的节点
137         else:
138             pre = self.head
139             while cur.next != self.head:
140                 # 找到了要删除的节点
141                 if cur.item == item:
142                     # 删除
143                     pre.next = cur.next
144                     return 
145                 else:
146                     pre = cur
147                     cur = cur.next
148             # cur 指向尾节点
149             if cur.item == item:
150                 # 尾部删除
151                 pre.next = cur.next
152             
153         
154 if __name__ == "__main__":
155     ll = SingleCycLinkList()
156     ll.add(1)
157     ll.add(2)
158     ll.append(3)
159     ll.append(7)
160     ll.insert(2, 4)
161     ll.insert(6, 5)
162     ll.insert(-1, 6)
163     print("length: ", ll.length())  # 7
164     ll.travel()  # 6 2 1 4 3 7 5
165     print(ll.search(3))  # True
166     print(ll.search(13))  # False
167     ll.remove(6)  
168     ll.travel()  # 2 1 4 3 7 5
169     

 

posted @ 2020-03-29 13:13  Juno3550  阅读(269)  评论(0编辑  收藏  举报