1. 鏈表概述
爲什麼需要鏈表?順序表的構建需要預先知道數據大小來申請連續的存儲空間,而在進行擴充時又需要進行數據的搬遷,所以使用起來並不是很靈活。鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理
。
鏈表(Linked list)是一種常見的基礎數據結構,是一種線性表,但是不像順序表一樣連續存儲數據,而是在每一個節點(數據存儲單元)裏存放下一個節點的位置信息(即地址)。
2. 鏈表與順序表的對比
鏈表失去了順序表隨機讀取的優點,同時鏈表由於增加了結點的指針域,空間開銷比較大,但對存儲空間的使用要相對靈活
。鏈表與順序表的各種操作複雜度如下所示:
雖然表面看起來複雜度都是 O(n),但是鏈表和順序表在插入和刪除時進行的是完全不同的操作。鏈表的主要耗時操作是遍歷查找,刪除和插入操作本身的複雜度是O(1)
。順序表查找很快,主要耗時的操作是拷貝覆蓋
。因爲除了目標元素在尾部的特殊情況,順序表進行插入和刪除時需要對操作點之後的所有元素進行前後移位操作,只能通過拷貝和覆蓋的方法進行。
3. 單向鏈表
單向鏈表也叫單鏈表,是鏈表中最簡單的一種形式,它的每個節點包含兩個域
,一個信息域(元素域)和一個鏈接域。這個鏈接指向鏈表中的下一個節點,而最後一個節點的鏈接域則指向一個空值。
表元素域elem用來存放具體的數據,鏈接域next用來存放下一個節點的位置(Python中的標識),變量p指向鏈表的頭節點(首節點)的位置,從p出發能找到表中的任意節點。
單鏈表中節點的實現:
class SingleNode:
def __init__(self, elem):
# 存放數據元素
self.item = elem
# 下一個節點的標識
self.next = None
單鏈表的操作有判斷鏈表是否爲空、獲取鏈表的長度、遍歷整個鏈表、鏈表頭部添加元素、鏈表尾部添加元素、指定位置添加元素、刪除節點和查找節點,下面實現這些操作:
class SingleNode:
def __init__(self, elem):
self.elem = elem
self.next = None
class SimpleLinkedList:
"""
單向鏈表
"""
def __init__(self, node=None):
"""
:param node:默認是空鏈表
"""
self.__head = node
def is_empty(self):
"""
判斷是否爲空
:return:
"""
return self.__head is None
def length(self):
"""
鏈表的長度
:return:count
"""
# 如果是空鏈表直接就返回count=0
cur = self.__head
count = 0
while cur is not None:
count += 1
cur = cur.next
return count
def travel(self):
"""
遍歷鏈表
:return:
"""
cur = self.__head
while cur is not None:
print(cur.elem, end=' ')
cur = cur.next
def add(self, elem):
"""
從鏈表的頭部添加元素
:param elem:
:return:
"""
node = SingleNode(elem)
"""
# 下面也可以處理是空鏈表的情況
if self.is_empty():
self.__head = node
node.next = self.__head
"""
node.next = self.__head
self.__head = node
def append(self, elem):
"""
尾部添加元素
:param elem:
:return:
"""
node = SingleNode(elem)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next is not None:
cur = cur.next
cur.next = node
def insert(self, pos, elem):
node = SingleNode(elem)
if pos <= 0:
self.add(elem)
return
elif pos > self.length() - 1:
self.append(elem)
return
pre = self.__head
count = 0
while count < pos - 1:
pre = pre.next
count += 1
node.next = pre.next
pre.next = node
def search(self, elem):
"""
查找元素
:param self:
:param elem:
:return:
"""
# 也可以處理空鏈表的情況
cur = self.__head
while cur is not None:
if cur.elem == elem:
return True
else:
cur = cur.next
return False
def remove(self, elem):
pre = None
cur = self.__head
# 適用於空節點和刪除頭節點
while cur is not None:
if cur.elem == elem:
# 判斷當前節點是否是頭節點
# 也可以解決鏈表中只有一個頭節點
if cur == self.__head:
self.__head = self.__head.next
# self.__head = cur.next
else:
# 也適用於刪除尾部節點
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
sll = SimpleLinkedList()
print('當前鏈表是否爲空:', sll.is_empty())
print('當前鏈表長度:', sll.length())
print('------在鏈表尾部添加節點------')
sll.append(1)
print('當前鏈表是否爲空:', sll.is_empty())
print('當前鏈表長度:', sll.length())
sll.append(2)
sll.append(3)
sll.append(4)
sll.append(5)
sll.append(6)
print('當前鏈表是否爲空:', sll.is_empty())
print('當前鏈表長度:', sll.length())
print('遍歷鏈表:', end='')
sll.travel()
print('\n------在鏈表頭部添加節點-----')
sll.add(7)
print('當前鏈表是否爲空:', sll.is_empty())
print('當前鏈表長度:', sll.length())
print('遍歷鏈表:', end='')
sll.travel()
print('\n------在指定位置插入節點-----')
sll.insert(0, 8)
sll.insert(2, 9)
sll.insert(9, 10)
sll.insert(10, 10)
print('當前鏈表是否爲空:', sll.is_empty())
print('當前鏈表長度:', sll.length())
print('遍歷鏈表:', end='')
sll.travel()
print('\n------刪除指定位置的元素,實現的效果類似列表的remove方法-----')
sll.remove(7)
sll.remove(1)
sll.remove(10)
print('當前鏈表是否爲空:', sll.is_empty())
print('當前鏈表長度:', sll.length())
print('遍歷鏈表:', end='')
sll.travel()
"""
當前鏈表是否爲空: True
當前鏈表長度: 0
------在鏈表尾部添加節點------
當前鏈表是否爲空: False
當前鏈表長度: 1
當前鏈表是否爲空: False
當前鏈表長度: 6
遍歷鏈表:1 2 3 4 5 6
------在鏈表頭部添加節點-----
當前鏈表是否爲空: False
當前鏈表長度: 7
遍歷鏈表:7 1 2 3 4 5 6
------在指定位置插入節點-----
當前鏈表是否爲空: False
當前鏈表長度: 11
遍歷鏈表:8 7 9 1 2 3 4 5 6 10 10
------刪除指定位置的元素,實現的效果類似列表的remove方法-----
當前鏈表是否爲空: False
當前鏈表長度: 8
遍歷鏈表:8 9 2 3 4 5 6 10
"""
4. 雙向鏈表
一種更復雜的鏈表是“雙向鏈表”或“雙面鏈表”。每個節點有兩個鏈接:一個指向前一個節點,當此節點爲第一個節點時,指向空值。而另一個指向下一個節點,當此節點爲最後一個節點時,指向空值。雙向鏈表引入 前驅節點和後繼節點
,實現的操作與單鏈表相同,只是需要考慮節點。
節點的實現:
class DoubleNnode:
def __init__(self, elem):
self.elem = elem
self.prev = None
self.next = None
雙向鏈表的實現:
class DoubleNode:
def __init__(self, elem):
self.elem = elem
self.prev = None
self.next = None
class DoubleLinkedList():
def __init__(self):
self.__head = None
def is_empty(self) -> bool:
"""
判斷是否爲空
:return:
"""
return self.__head is None
def length(self) -> int:
"""
鏈表的長度
:return:count
"""
# 如果是空鏈表直接就返回count=0
cur = self.__head
count = 0
while cur is not None:
count += 1
cur = cur.next
return count
def travel(self):
"""
遍歷鏈表
:return:
"""
cur = self.__head
while cur is not None:
print(cur.elem, end=' ')
cur = cur.next
def add(self, item):
"""
頭插法
:param item:
:return:
"""
node = DoubleNode(item)
if self.is_empty():
self.__head = node
return
node.next = self.__head
self.__head = node
node.next.prev = node
def append(self, elem):
"""
尾部添加元素
:param elem:
:return:
"""
node = DoubleNode(elem)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next is not None:
cur = cur.next
cur.next = node
node.prev = cur
def insert(self, pos, elem):
node = DoubleNode(elem)
if pos <= 0:
self.add(elem)
elif pos > self.length() - 1:
self.append(elem)
else:
cur = self.__head
count = 0
while count < pos:
cur = cur.next
count += 1
# 第一種方式
node.next = cur
node.prev = cur.prev
cur.prev.next = node
cur.prev = node
# 第二種方式
"""
cur.prev = node
node.prev.next = node
"""
def search(self, elem) -> bool:
cur = self.__head
while cur is not None:
if cur.elem == elem:
return True
else:
cur = cur.next
return False
def remove(self, elem):
cur = self.__head
while cur is not None:
if cur.elem == elem:
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
break
else:
cur = cur.next
dll = DoubleLinkedList()
print('-------初始的鏈表------')
print('當前鏈表是否爲空:', dll.is_empty())
print('當前鏈表的長度:', dll.length())
print('------頭插法添加鏈表------')
dll.add(1)
dll.add(2)
dll.add(3)
print('當前鏈表是否爲空:', dll.is_empty())
print('當前鏈表的長度:', dll.length())
print('遍歷鏈表:', end='')
dll.travel()
print('\n------尾插法添加鏈表------')
dll.append(1)
dll.append(2)
dll.append(3)
dll.append(4)
print('當前鏈表是否爲空:', dll.is_empty())
print('當前鏈表的長度:', dll.length())
print('遍歷鏈表:', end='')
dll.travel()
print('\n------指定位置插入------')
dll.insert(0, 6)
dll.insert(2, 7)
dll.insert(5, 8)
print('當前鏈表是否爲空:', dll.is_empty())
print('當前鏈表的長度:', dll.length())
print('遍歷鏈表:', end='')
dll.travel()
print('\n------查找元素------')
print('當前鏈表是否爲空:', dll.is_empty())
print('當前鏈表的長度:', dll.length())
print('遍歷鏈表:', end='')
dll.travel()
print('\n鏈表中是否存在元素是2的節點:%s' % dll.search(2))
print('------刪除元素(這裏以刪除頭節點6爲例子)------')
dll.remove(6)
print('當前鏈表是否爲空:', dll.is_empty())
print('當前鏈表的長度:', dll.length())
print('遍歷鏈表:', end='')
dll.travel()
"""
-------初始的鏈表------
當前鏈表是否爲空: True
當前鏈表的長度: 0
------頭插法添加鏈表------
當前鏈表是否爲空: False
當前鏈表的長度: 3
遍歷鏈表:3 2 1
------尾插法添加鏈表------
當前鏈表是否爲空: False
當前鏈表的長度: 7
遍歷鏈表:3 2 1 1 2 3 4
------指定位置插入------
當前鏈表是否爲空: False
當前鏈表的長度: 10
遍歷鏈表:6 3 7 2 1 8 1 2 3 4
------查找元素------
當前鏈表是否爲空: False
當前鏈表的長度: 10
遍歷鏈表:6 3 7 2 1 8 1 2 3 4
鏈表中是否右元素是2的節點:True
------刪除元素------
當前鏈表是否爲空: False
當前鏈表的長度: 9
遍歷鏈表:3 7 2 1 8 1 2 3 4
"""
5. 單向循環鏈表
單鏈表的一個變形是單向循環鏈表,鏈表中最後一個節點的next域不再爲None,而是指向鏈表的頭節點。
節點的創建:
class Node:
def __init__(self, elem):
self.elem = elem
self.next = None
單向循環鏈表的實現:
class Node:
def __init__(self, elem):
self.elem = elem
self.next = None
class CircularlinkedList:
def __init__(self, node=None):
self.__head = node
if node:
node.next = node
def is_empty(self):
"""
判斷是否爲空
:return:
"""
return self.__head == None
def lencllth(self):
"""
獲取鏈表的長度
:return:
"""
# 如果是空鏈表
if self.is_empty():
return 0
# 只有一個節點,也是可以處理的
cur = self.__head
count = 1
while cur.next != self.__head:
count += 1
cur = cur.next
return count
def travel(self):
"""
遍歷所有節點中的元素
:return:
"""
# 如果是空鏈表則不作任何操作
if self.is_empty():
return
# 如果只有一個節點也是可以處理的
cur = self.__head
while cur.next != self.__head:
print(cur.elem, end=' ')
cur = cur.next
print(cur.elem)
def add(self, elem):
"""
頭部插入
:param elem:
:return:
"""
node = Node(elem)
# 如果是空鏈表
if self.is_empty():
self.__head = node
node.next = node
return
# 只有一個節點,也是可以處理的
cur = self.__head
while cur.next != self.__head:
cur = cur.next
node.next = self.__head
self.__head = node
# cur.next = node
cur.next = self.__head
def append(self, elem):
"""
尾部插入
:param elem:
:return:
"""
node = Node(elem)
# 鏈表爲空
if self.is_empty():
self.__head = node
node.next = node
return
# 也適用於鏈表只有一個節點
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, elem):
if pos <= 0:
self.add(elem)
elif pos > self.lencllth() - 1:
self.append(elem)
else:
# 因爲不涉及頭節點和尾節點的鏈接,中間插入和單鏈表插入方式一樣
count = 0
pre = self.__head
while count < pos - 1:
count += 1
pre = pre.next
# 循環退出之後,pre指向post-1的位置
node = Node(elem)
node.next = pre.next
pre.next = node
def search(self, elem):
if self.is_empty():
return False
cur = self.__head
while cur != self.__head:
if cur.elem == elem:
return True
cur = cur.next
if cur.elem == elem:
return True
return False
def remove(self, elem):
if self.is_empty():
return
cur = self.__head
prev = None
while cur.next != self.__head:
if cur.elem == elem:
# 判斷是否是頭節點
if cur == self.__head:
rear = self.__head
# 通過循環找到尾節點
while rear.next != self.__head:
rear = rear.next
self.__head = cur.next
rear.next = self.__head
else:
# 刪除中間節點
prev.next = cur.next
return
prev = cur
cur = cur.next
# 上述循環之後,最後一個節點的元素沒有判斷,所以需要判斷
if cur.elem == elem:
# 如果鏈表只有一個節點
if cur.next == self.__head:
self.__head = None
# prev.next = cur.next
prev.next = self.__head
cll = CircularlinkedList()
print('------鏈表初始化信息-----')
print('當前鏈表是否爲空:', cll.is_empty())
print('當前鏈表長度:', cll.lencllth())
print('------在鏈表尾部添加節點------')
cll.append(1)
cll.append(2)
cll.append(3)
cll.append(4)
cll.append(5)
cll.append(6)
print('當前鏈表是否爲空:', cll.is_empty())
print('當前鏈表長度:', cll.lencllth())
print('遍歷鏈表:', end='')
cll.travel()
print('\n------在鏈表頭部添加節點-----')
cll.add(7)
print('當前鏈表是否爲空:', cll.is_empty())
print('當前鏈表長度:', cll.lencllth())
print('遍歷鏈表:', end='')
cll.travel()
print('\n------在指定位置插入節點-----')
cll.insert(0, 8)
cll.insert(2, 9)
cll.insert(9, 10)
cll.insert(10, 10)
print('當前鏈表是否爲空:', cll.is_empty())
print('當前鏈表長度:', cll.lencllth())
print('遍歷鏈表:', end='')
cll.travel()
print('\n------刪除指定位置的元素,實現的效果類似列表的remove方法-----')
cll.remove(8)
cll.remove(1)
cll.remove(10)
print('當前鏈表是否爲空:', cll.is_empty())
print('當前鏈表長度:', cll.lencllth())
print('遍歷鏈表:', end='')
cll.travel()
"""
------鏈表初始化信息-----
當前鏈表是否爲空: True
當前鏈表長度: 0
------在鏈表尾部添加節點------
當前鏈表是否爲空: False
當前鏈表長度: 6
遍歷鏈表:1 2 3 4 5 6
------在鏈表頭部添加節點-----
當前鏈表是否爲空: False
當前鏈表長度: 7
遍歷鏈表:7 1 2 3 4 5 6
------在指定位置插入節點-----
當前鏈表是否爲空: False
當前鏈表長度: 11
遍歷鏈表:8 7 9 1 2 3 4 5 6 10 10
------刪除指定位置的元素,實現的效果類似列表的remove方法-----
當前鏈表是否爲空: False
當前鏈表長度: 8
遍歷鏈表:7 9 2 3 4 5 6 10
"""
Github:https://github.com/ThanlonSmith/Data-Structure-Python3