python實現鏈表的操作
鏈表的介紹
鏈表(linked_list)是物理存儲單元上非連續的、非順序的存儲結構,數據元素的邏輯順序是通過鏈表的指針地址實現,每個元素包含兩個結點,一個是存儲元素的數據域 (內存空間),另一個是指向下一個結點地址的指針域。根據指針的指向,鏈表能形成不同的結構,例如單鏈表,雙向鏈表,循環鏈表等。
鏈表通過將鏈點 i 與其鄰居鏈點 i+1 通過指針相關聯,從索引 0 到索引 N-1 對鏈點進行排序。
複雜度分析
鏈表屬於常見的一種線性結構,對於插入和移除而言,時間複雜度都爲 O(1)
但是對於搜索操作而言,不管從鏈表的頭部還是尾部,都需要遍歷 O(n),所以最好複雜度爲 O(1),最壞的情況就是從頭部遍歷到尾部才搜索出對應的元素,所以最壞複雜度爲 O(n),平均複雜度爲 O(n)。
歸納如下:
最好複雜度爲 O(1)
最壞複雜度爲 O(n)
平均複雜度爲 O(n)
單鏈表的功能是實現
- 創建 Node 類
創建一個 Node 的類,作爲基礎數據結構:鏈點,並初始化對應的內參。
class Node:
def __init__(self, data):
self.data = data # 表示對應的元素值
self.next = None # 表示下一個鏈接的鏈點
- 創建 Linked_List 類
創建一個 Linked_List 的類,並初始化對應的內參。
class Linked_List:
def __init__(self, head=None): # 鏈表初始化函數
self.head = head # 表示鏈表的頭部元素
- 添加 append 函數
添加一個 append 的函數,功能是向鏈表添加新的結點
def append(self, new_element):
# 將頭部結點指向臨時變量 current
current = self.head
# 當頭部結點存在時
if self.head:
# 循環遍歷到鏈表的最後一個元素
while current.next:
current = current.next
current.next = new_element
# 當頭部結點不存在時
else:
self.head = new_element
- 添加 is_empty 函數
添加一個 is_empty 的函數,功能是判斷鏈表是否爲空
def is_empty(self):
"""
判斷鏈表是否爲空
"""
# bool() 函數只返回 True 和 False
return not self.head
- 添加 insert 函數
insert(new_element) 往鏈表中任意位置添加一個 new_element 元素
流程如下:
1 先判斷要插入的位置是否在鏈表的索引範圍內。
2 當插入的位置是頭結點(即索引爲 0)時,做特殊情況處理。
3 當要插入結點的位置不在 0 時,找到要插入的位置,插入新結點
def insert(self, position, new_element):
"""
在鏈表中指定索引處插入元素
"""
if position < 0 or position > self.get_length():
raise IndexError('insert 插入時,key 的值超出了範圍')
temp = self.head
# 將插入元素的 next 屬性指向老的頭結點,並將新的元素賦值給頭結點
if position == 0:
new_element.next = temp
self.head = new_element
# new_element.next, self.head = temp, new_element
return
i = 0
# 遍歷找到索引值爲 position 的結點後, 在其後面插入結點
while i < position:
pre = temp
temp = temp.next
# pre, temp = temp, temp.next
i += 1
pre.next = new_element
new_element.next = temp
# pre.next, new_element.next = new_element, temp
- 添加 remove 函數
remove() 從鏈表中任意位置刪除一個元素
流程如下:
1.先判斷要刪除的元素索引是否存在,如果不存在拋出錯誤
2 接着判斷當存在鏈表元素時才能執行刪除操作。
3 當要刪除的是頭結點時(即索引爲 0),做特殊情況處理。
4 其他情況時,通過循環找到要刪除的結點。
5 最後要做的就是把這個結點刪除掉。
def remove(self, position):
"""
刪除指定索引的鏈表元素
"""
if position < 0 or position > self.get_length()-1:
# print("insert error")
raise IndexError('刪除元素的索引超出範圍')
i = 0
temp = self.head
# 當存在鏈表元素時才能執行刪除操作
while temp != None:
# 將頭結點的後一個結點賦值給新的頭結點,再將之前的頭結點指向 `None`
if position == 0:
self.head = temp.next
temp.next = None
return
pre = temp
# 以此來遍歷鏈表
temp = temp.next
# pre, temp = temp, temp.next
i += 1
if i == position:
# 將 pre 的 next 屬性指向 temp 的下一個結點
pre.next = temp.next
temp.next = None
# pre.next, temp.next = temp.next, None
return
- 添加其他函數
get_length:獲取鏈表的長度
print_list:遍歷鏈表,並將元素依次打印出來
reverse:將鏈表反轉,可以參考鏈接https://blog.csdn.net/blioo/article/details/62050967
initlist: 將列表轉換爲鏈表
def get_length(self):
"""
返回鏈表的長度
"""
頭部結點賦值給頭部結點
temp = self.head
# 計算鏈表的長度變量
length = 0
while temp != None:
length = length+1
temp = temp.next
# 返回鏈表的長度
return length
def print_list(self):
"""
遍歷鏈表,並將元素依次打印出來
"""
print("linked_list:")
# 頭部結點賦值給臨時變量 temp
temp = self.head
while temp is not None:
print(temp.data)
temp = temp.next
def reverse(self):
"""
將鏈表反轉
"""
prev = None
current = self.head
while current:
next_node = current.next
current.next = prev
prev = current
current = next_node
self.head = prev
def initlist(self,data_list):
"""
將列表轉換爲鏈表
"""
# 創建頭結點
self.head = Node(data_list[0])
temp = self.head
# 逐個爲 data 內的數據創建結點, 建立鏈表
for i in data_list[1:]:
node = Node(i)
temp.next = node
temp = temp.next
完整代碼如下
class Node:
def __init__(self,data):
#表示對應的元素值
self.data=data
#表示下一個鏈接的鏈點
self.next=None
class Linked_List:
#鏈表初始化函數
def __init__(self,head=None):
#表示鏈表的頭部元素
self.head=head
def append(self,new_element):
'''
在鏈表後面增加一個元素
:param new_element: 增加的新元素
:return: 返回值
'''
#將頭部結點指向臨時變量current
current=self.head
#當頭部結點存在時
if self.head:
#循環遍歷到鏈表的最後一個元素
while current.next:
#指針指向下一個元素
current=current.next
#在最後一個結點添加新元素
current.next=new_element
#當頭部結點不存在時
else:
#新元素賦值頭結點
self.head=new_element
def is_empty(self):
#判斷鏈表是否爲空
return not self.head
def get_length(self):
'''獲取鏈表的長度'''
#臨時變量指向隊列頭部
temp=self.head
#計算鏈表的長度變量
length=0
#循環,直至爲空終止
while temp!=None:
#累加計數
length=length+1
#臨時變量指向下一元素
temp=temp.next
#返回鏈表的長度
return length
def insert(self,position,new_element):
'''
在鏈表中的指定位置插入新元素
:param position: 指定位置
:param new_element: 新元素
:return: 返回
'''
#判斷要插入的位置是否在鏈表的索引範圍
if position<0 or position > self.get_length():
raise IndexError("insert 插入時,key的值超出了範圍")
#將頭部結點指向臨時變量temp
temp=self.head
#當插入的位置是頭結點是,做特殊情況處理
if position==0:
#新元素的next指向頭結點
new_element.next=temp
#新元素即新的頭結點
self.head=new_element
return
#當插入的結點位置不在0時,找到要插入的位置,插入新節點
i=0
#遍歷找到索引值爲position的節點後,在其後插入結點
while i<position:
#臨時變量pre,暫存上一個結點,用於保存要插入的前一個結點
pre=temp
#指向下一結點
temp=temp.next
#pre,temp=temp,temp.next 上面兩式的簡寫,效果一樣
#計數,找位置
i+=1
#前一結點的後面插入新元素
pre.next=new_element
#新元素後面鏈接temp即原來pre的下一個結點
new_element.next=temp
#pre.next,new_element=new_element,temp 上面兩式的簡寫,效果一樣
def print_list(self):
'''遍歷鏈表,並將元素打印出來'''
print("linked_list:")
# 將頭部結點指向臨時變量temp
temp=self.head
# 建立新列表
new_list=[]
#循環添加鏈表的元素到列表中,直到鏈表爲空終止
while temp is not None:
new_list.append(temp,data)
temp=temp.next
print(new_list)
def remove(self,position):
'''刪除指定索引的鏈表元素'''
#判斷要刪除的元素索引是否存在,如果不存在拋出錯誤
if position<0 or position>self.get_length()-1:
raise IndexError("刪除的元素索引超出範圍")
i=0
# 將頭部結點指向臨時變量temp
temp=self.head
#遍歷找到索引值爲position的結點
while temp!=None:
#要刪除的是頭結點是,做特殊情況處理
if position==0:
#將頭結點的後一個結點賦值給新的頭結點
self.head=temp.next
#再將之前的頭結點指向None
temp.next=None
return True
#pre暫存目標結點的前一個結點
pre=temp
temp=temp.next
#位置計數
i+=1
#找到目標元素,處理
if i==position:
#前一結點指向當前結點的下一個結點
pre.next=temp.next
#當前結點下一鏈接爲空
temp.next=None
return
def reverse(self):
'''將鏈表反轉'''
prev=None
current=self.head
while current:
next_node=current.next
current.next=prev
prev=current
current=next_node
self.head=prev
def initllist(self,data_list):
'''將列表轉化爲鏈表'''
#創建頭結點
self.head=Node(data_list[0])
#臨時變量指向頭結點
temp=self.head
#爲data內的數據創建結點,建立鏈表
for i in data_list[1:]:
#將列表的值賦值結點node
node=Node(i)
#當前結點鏈接到鏈表
temp.next=node
#指針指向下一個
temp=temp.next
雙鏈表
雙向鏈表(Double_linked_list)也叫雙鏈表,是鏈表的一種,它的每個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。
完整代碼如下
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 get_length(self):
# 返回鏈表的長度
#臨時變量cur指向頭結點
cur = self._head
#計數鏈表長度
count = 0
#循環到鏈表爲空
while cur != None:
#計數器加1
count = 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():
# 如果是空鏈表,將 node 賦值給 _head
self._head = node
else:
# 將 node 的 next 屬性指向頭節點 _head
node.next = self._head
# 將頭節點 _head 的 prev 屬性指向 node
self._head.prev = node
# 將 node 賦值給 _head
self._head = node
def append(self, item):
# 尾部插入元素即尾插法
node = Node(item)
if self.is_empty():
# 如果是空鏈表,將 node 賦值給 _head
self._head = node
else:
# 循環移動到鏈表尾部結點的位置
cur = self._head
while cur.next != None:
cur = cur.next
# 將尾結點 cur 的 next 屬性指向 node
cur.next = node
# 將 node 的 prev 屬性指向 cur
node.prev = cur
def search(self, item):
# 查找元素是否存在
#臨時變量指向鏈表頭結點
cur = self._head
#遍歷查找元素,視情況返回true或false
while cur != None:
if cur.item == item:
return True
cur = cur.next
return False
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.prev = cur
# 將 node 的 next 屬性指向 cur 的下一個節點
node.next = cur.next
# 將 cur 的下一個節點的 prev 屬性指向 node
cur.next.prev = node
# 將 cur 的 next 指向 node
cur.next = node
def remove(self, item):
# 刪除元素
if self.is_empty():
return
else:
cur = self._head
if cur.item == item:
# 如果首節點的元素即是要刪除的元素
if cur.next == None:
# 如果鏈表只有這一個節點
self._head = None
else:
# 將第二個節點的 prev 屬性設置爲 None
cur.next.prev = None
# 將 _head 指向第二個節點
self._head = cur.next
return
while cur != None:
if cur.item == item:
# 將 cur 的前一個節點的 next 指向 cur 的後一個節點
cur.prev.next = cur.next
# 將 cur 的後一個節點的 prev 指向 cur 的前一個節點
cur.next.prev = cur.prev
break
cur = cur.next
實例應用
目標:
交換兩個鏈點在鏈表中的位置
不改變其他鏈點在鏈表中的位置
思路:
採用 insert 的思想,對於要交換的兩個鏈點 d1 d2,各自聲明新的 D1 D2 ,使 D1=d1, D2=d2
然後把 然後再根據 d1 d2 的位置,改變索引的位置,即完成交換的全部操作
交換單鏈表裏兩個鏈點
在這個要求中,我們要給定兩個值,如果這兩個值都在單鏈表的鏈點中,即交換這兩個鏈點在單鏈表的位置。
舉例:
1->2->3->4->5
input:1 4 output:4->2->3->1->5
完整代碼如下
class Node:
#初始化結點
def __init__(self, data):
#初始化結點的數據位傳入的值
self.data = data
#初始結點的下一結點鏈接爲空
self.next = None
class Linkedlist:
#初始化鏈表
def __init__(self):
#初始頭結點爲空
self.head = None
def print_list(self): # 遍歷鏈表,並將元素依次打印出來
print("linked_list:")
#指針指向單鏈表頭結點
temp = self.head
#初始化列表
new_list = []
#遍歷添加鏈表元素到列表
while temp is not None:
new_list.append(temp.data)
temp = temp.next
#打印列表
print(new_list)
#添加新元素到鏈表
def insert(self, new_data):
#初始化新節點
new_node = Node(new_data)
#頭插法插入新節點
new_node.next = self.head
self.head = new_node
#交換指定的元素的位置
def swapNodes(self, d1, d2):
#暫存目標元素的前一個結點,初始爲空
prevD1 = None
prevD2 = None
#如果交換元素相等,返回不執行操作
if d1 == d2:
return
else:
#D1指向單鏈表頭結點
D1 = self.head
#遍歷鏈表查找目標元素
while D1 is not None and D1.data != d1:
#暫存前一個結點
prevD1 = D1
#指向下一結點
D1 = D1.next
D2 = self.head
while D2 is not None and D2.data != d2:
prevD2 = D2
D2 = D2.next
#如何兩個目標元素均不存在,返回
if D1 is None or D2 is None:
return
# 如果D1存在前一結點,則D1存在已找到,並把前一結點的後驅指向D2
if prevD1 is not None:
prevD1.next = D2
#如果D1沒有前一結點,則D1爲頭結點,賦值D2新的頭結點
else:
self.head = D2
if prevD2 is not None:
prevD2.next = D1
else:
self.head = D1
#完成鏈表的鏈接處理
temp = D1.next
D1.next = D2.next
D2.next = temp
if __name__ == '__main__':
#初始化鏈表
list = Linkedlist()
#鏈表添加結點
list.insert(5)
list.insert(4)
list.insert(3)
list.insert(2)
list.insert(1)
#打印初始鏈表
list.print_list()
#交換指定鏈表值的位置
list.swapNodes(1, 5)
#打印交換後的鏈表
print("After swapping")
list.print_list()
運行結果如下
思路分析
1 初始化鏈表,並添加數據
2 數據交換
第一步:判斷要交換的兩個元素,是否相等,相等返回,不交換
第二步:遍歷目標元素,並保存其前一結點
考慮幾種情況
@1:兩個均未找到或者只找到一個,不操作,原鏈表輸出
@2:兩者均找到,執行,交換後輸出
第三步:打印前後鏈表進行比對’