1. 鏈表
首先要明白一點:之所以做元素互換時,只有python裏纔可以直接用 a,b=b,a ——> 就是因爲做a=10時,a可以看做是一個輸入待定的函數,10並不是直接放入到了a裏面,二者只是一個指向關係
注:鏈表與之前的順序表對比
(因爲鏈表只能記錄頭節點,故要search到其中的元素,就要從頭開始遍歷往後找,故訪問元素的時間複雜度是O(n); 而順序表可以直接找到目標元素,故訪問元素的時間複雜度是O(1),但除了在尾部的情況外,做刪除或插入時,都要把操作點位置後的所有元素依次後移,故對應的爲O(n))
(1). 單鏈表
class Node(object):
def __init__(self,data):
self.data = data
self.next = None#指向待定
class SingleLinkList(object):#表頭只有data,表尾next爲None
def __init__(self,node=None):#待接節點待定
self.__head = node#頭節點爲私有屬性(必須雙下劃綫),限鏈表內使用
def is_empty(self):
return self.__head == None#頭空,則鏈表空
def length(self):
cur = self.__head#cur遊標,用來移動遍歷節點
count = 0
while cur != None:
count += 1
cur = cur.next
return count#當鏈表空,則頭空,cur=None,不會循環,則count=0,符合
def travel(self):#遍歷
cur = self.__head
while cur != None:
print(cur.data, end=' ')
cur = cur.next
print('')
def add_front(self,item):#鏈首加元素,頭不變
node = Node(item)
'鏈首本身沒有next,等號代表指向,self.__head本身指向的是原head後的第一個節點,現在要把該節點接到新的節點的後面'
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=self.head=None ,沒cur.next,故需另做判斷
cur = cur.next
cur.next = node
def insert(self,pos,item):#指定位置加元素
if pos <= 0:
self.add_front(item)#鏈首
elif pos > (self.length()-1):
self.append(item)#鏈尾
else:
pre = self.__head#遊標從頭節點起步
count = 0
"""
此處有個難點:比如pos=2,即要插入到第3個位置(不算頭結點),由於插入位置的前個節點的指向也需更改,
故條件取到count < (pos-1) 而不是count < pos
"""
while count < (pos-1):
count += 1
pre = pre.next
#退出循環時,pre指向pos-1位置
node = Node(item)
node.next = pre.next#pre.next是被新節點佔去位置的原節點,將其放到新節點的後面
pre.next = node
def remove(self,item):#刪除節點
'要考慮鏈首、尾、爲空'
cur = self.__head
pre = None #遊標cur的前個節點
while cur != None:
if cur.data == item:
'先判斷該節點是否爲頭結點'
if cur == self.__head:
self.__head = cur.next#當只有頭結點,將其刪除後,self.__head就該是None,而cur.next就是None
else:
"""
注意:pre本身原是無節點意義的,是因爲在判斷條件下會先經歷
pre = cur cur = cur.next 的位置後移,所以pre也有了和cur一樣的節點屬性!
"""
pre.next = cur.next#把待刪除節點的下個節點指給待刪除節點的前個節點
break#必須有!
else:
pre = cur
cur = cur.next#pre與cur同時後移
def search(self,item):#查找某節點是否存在
cur = self.__head
while cur != None:
if cur.data == item:
return True
else:
cur = cur.next
return False
if __name__ == '__main__':
ll = SingleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.add_front(8)#8 1 2 3 4
ll.insert(-1, 9)#9 8 1 2 3 4
ll.travel()
ll.insert(2, 100)#9 8 100 1 2 3 4
ll.travel()
ll.insert(10, 200)#9 8 100 1 2 3 4 200
ll.travel()
ll.remove(9)#刪頭
ll.travel()#8 100 1 2 3 4 200
ll.remove(200)#刪尾
ll.travel()#8 100 1 2 3 4
ll.remove(1)
ll.travel()#8 100 2 3 4
(2). 單向循環鏈表
class Node(object):
def __init__(self, data):
self.data = data
self.next = None # 指向待定
'單向循環列表'
class SingleCycleLinkList(object):
def __init__(self, node=None): # 待接節點待定
self.__head = node # 頭節點爲私有屬性(必須雙下劃綫),限鏈表內使用
if node:
node.next = node # 若鏈表在初始時就已經傳入了節點,則next指向自己
def is_empty(self):
return self.__head == None # 頭空,則鏈表空
def length(self):
if self.is_empty():
return 0 # 函數遇return則執行結束
cur = self.__head # 遊標
count = 1 # 若非空,則至少有頭節點,計數從1起
while cur.next != self.__head: # 至尾結束
count += 1
cur = cur.next
return count
def travel(self): # 遍歷
if self.is_empty(): # 當空,cur = self.__head=None,但None不具備next屬性,故空鏈要單獨列出
return
cur = self.__head
while cur.next != self.__head:
print(cur.data, end=' ')
cur = cur.next
print(cur.data) # 尾節點在循環退出時沒打出,故補上
def add_front(self, item): # 鏈首加元素,頭不變
node = Node(item)
'鏈首本身沒有next,等號代表指向,self.__head本身指向的是原head後的第一個節點,現在要把該節點接到新的節點的後面'
if self.is_empty():
self.__head = node
node.next = node
return
cur = self.__head
while cur.next != self.__head:
cur = cur.next
# 退出循環,cur此時爲尾節點
node.next = self.__head # node接到鏈首,此時self.__head更新後指向的是node了
self.__head = node # 頭結點接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 = self.__head
cur.next = node
def insert(self, pos, item): # 指定位置加元素
if pos <= 0:
self.add_front(item) # 鏈首
elif pos > (self.length() - 1):
self.append(item) # 鏈尾
else:
pre = self.__head # 遊標從頭節點起步
count = 0
"""
此處有個難點:比如pos=2,即要插入到第3個位置(不算頭結點),由於插入位置的前個節點的指向也需更改,
故條件取到count < (pos-1) 而不是count < pos
"""
while count < (pos - 1):
count += 1
pre = pre.next
# 退出循環時,pre指向pos-1位置
node = Node(item)
node.next = pre.next # pre.next是被新節點佔去位置的原節點,將其放到新節點的後面
pre.next = node
def remove(self, item): # 刪除節點
'要考慮鏈首、尾、爲空'
if self.is_empty():
return
cur = self.__head
pre = None # 遊標cur的前個節點
while cur.next != self.__head:
if cur.data == item:
'先判斷該節點是否爲頭結點'
if cur == self.__head: # =是指向,==不是
# 當待刪爲頭結點,則先找尾結點
rear = self.__head
while rear.next != self.__head:
rear = rear.next # 後移, 直至找到尾節點
self.__head = cur.next # cur此時就是頭結點,把頭指向鏈首cur.next,即鏈首成了新的頭結點了
rear.next = self.__head # 尾指向新的頭
else: # 中間節點
"""
注意:pre本身原是無節點意義的,是因爲在判斷條件下會先經歷
pre = cur cur = cur.next 的位置後移,所以pre也有了和cur一樣的節點屬性!
"""
pre.next = cur.next # 把待刪除節點的下個節點指給待刪除節點的前個節點
'此處不能像之前用break!因爲break只是退出了while循環,而此處需要函數結束'
return
else:
pre = cur
cur = cur.next # pre與cur同時後移
# 退出循環,cur此時爲尾節點
if cur.data == item:
if cur == self.__head: # 只有頭結點
self.__head = None
else:
pre.next = cur.next # cur的前節點直接指向cur的後節點
def search(self, item): # 查找某節點是否存在
if self.is_empty():
return False
cur = self.__head
while cur.next != self.__head:
if cur.data == item:
return True
else:
cur = cur.next
# 退出循環,cur此時爲尾節點
if cur.data == item:
return True
return False
if __name__ == '__main__':
ll = SingleCycleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.add_front(8) # 8 1 2 3 4
ll.insert(-1, 9) # 9 8 1 2 3 4
ll.travel()
ll.insert(2, 100) # 9 8 100 1 2 3 4
ll.travel()
ll.insert(10, 200) # 9 8 100 1 2 3 4 200
ll.travel()
ll.remove(9) # 刪頭
ll.travel() # 8 100 1 2 3 4 200
ll.remove(200) # 刪尾
ll.travel() # 8 100 1 2 3 4
ll.remove(1)
ll.travel() # 8 100 2 3 4
(3). 雙向鏈表
class Node(object):
def __init__(self,data):
self.data = data
self.prev = None#前驅節點
self.next = None#後置節點
'雙向鏈表'
class DoubleLinkList(object):
def __init__(self,node=None):#待接節點待定
self.__head = node#頭節點爲私有屬性(必須雙下劃綫),限鏈表內使用
def is_empty(self):
return self.__head == None#頭空,則鏈表空
def length(self):
cur = self.__head#cur遊標,用來移動遍歷節點
count = 0
while cur != None:
count += 1
cur = cur.next
return count#當鏈表空,則頭空,cur=None,不會循環,則count=0,符合
def travel(self):#遍歷
cur = self.__head
while cur != None:
print(cur.data, end=' ')
cur = cur.next
print('')
"""
由於雙向鏈表的以上前4個屬性及search()與單鏈表一樣,所以可以直接用面向對象的繼承方法,寫作如下:
class DoubleLinkList(SingleLinkList)
則以上5個屬性就無需再寫了,因爲直接繼承過來了
"""
def add_front(self,item):#鏈首加元素,頭不變
node = Node(item)
'鏈首本身沒有next,等號代表指向,self.__head本身指向的是原head後的第一個節點,現在要把該節點接到新的節點的後面'
node.next = self.__head
self.__head = node
node.next.prev = node#比單鏈多一個node的下個節點對node的指向
def append(self,item):#鏈表尾加元素
node = Node(item)#待加節點
if self.is_empty():
self.__head=node
else:
cur = self.__head
while cur.next != None:#找到表尾;當表爲空,cur=self.head=None ,沒cur.next,故需另做判斷
cur = cur.next
cur.next = node
node.prev = cur#比單鏈多一個cur的下個節點node對cur的指向
def insert(self,pos,item):#指定位置加元素
if pos <= 0:
self.add_front(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 = node
node.prev.next = node
"""
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.data == item:
'先判斷該節點是否爲頭結點'
if cur == self.__head:
self.__head = cur.next#當只有頭結點,將其刪除後,self.__head就該是None,而cur.next就是None
if cur.next:#當只有cur這一個節點的時候,cur.next爲None,是沒有prev的
cur.next.prev = None
else:
cur.prev.next = cur.next#把待刪除節點的下個節點指給待刪除節點的前個節點
if cur.next:
cur.next.prev = cur.prev
break#必須有!
else:
cur = cur.next#pre與cur同時後移
def search(self,item):#查找某節點是否存在
cur = self.__head
while cur != None:
if cur.data == item:
return True
else:
cur = cur.next
return False
if __name__ == '__main__':
ll = DoubleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.add_front(8)#8 1 2 3 4
ll.insert(-1, 9)#9 8 1 2 3 4
ll.travel()
ll.insert(2, 100)#9 8 100 1 2 3 4
ll.travel()
ll.insert(10, 200)#9 8 100 1 2 3 4 200
ll.travel()
ll.remove(9)#刪頭
ll.travel()#8 100 1 2 3 4 200
ll.remove(200)#刪尾
ll.travel()#8 100 1 2 3 4
ll.remove(1)
ll.travel()#8 100 2 3 4