实现单链表--Python
單鏈表的結構
單鏈表結點的組成:
在單鏈表里,表里的n 個結點通過鏈接形成一條結點鏈。從表里的任一個結點都可以找到保存下一個元素的結點。
鏈表中的一個重要部分就是頭指針。頭指針保存著鏈表首結點的標識,通過頭指針可以十分方便的對鏈表進行:訪問元素、遍歷、增刪改查等操作
TODO:補充單鏈表的特性
實現
定義節點類
節點是鏈表的基本組成部分
class Node : # 只定義初始化操作def __init__(self, elm, next_=None):self.elem = elem# 因為Python中next是一個內置的函數,為區分,故在變量名后添加"_"self.next_ = next_定義單鏈表類
# 單鏈表類只有一個 _head 域,表示單鏈表的頭指針 class SingleList:def __init__(self):self._head = None變量前的單下劃線表示私有變量,不要在對象的外部去訪問。python語言沒有為定義私有變量提供專門的機制,只能通過約定和良好的編程習慣來保護對象的私有屬性。
定義異常類
為了能合理處理一些鏈表操作中遇到的錯誤狀態,如在執行方法時遇到了無法操作的錯誤參數,首先定義一個異常類LinkedListUnderflow,這里將它定義為標準異常類ValueError的子類,pass表示空語句,即什么都不做。
TODO: 在這里直接拋出ValueError也沒問題,但定義了自己的異常類就可以寫專門的異常處理器。
class LinkedListUnderflow(ValueError):pass對鏈表的具體操作
刪除鏈表
在Python中,如果一個對象未被引用,則該對象會被當做垃圾并進行回收。所以刪除鏈表只需將頭指針設為None,使得鏈表對象不被引用
def del_list(self):self._head = None判斷表空
在Python里檢查_head值是否為None,如果為None,則說明_head不指向任何對象
def is_empty(self):return self._head is None判斷表滿
因為鏈表沒有一個固定的容量,可以動態擴充,所以在內容充足的情況下,鏈表是不會滿的
鏈表長度
算法描述:
代碼實現:
def length(self):'''計算鏈表長度'''point = self._headcount = 1while point.next_ != None:point = point.next_count += 1return count思考1:為什么需要新建一個point變量
遍歷鏈表
算法描述:
算法實現:
def traverse(self):'''遍歷鏈表'''point = self._headindex = 0while point.next_ != None:index += 1print("第", index, "個元素是:", point.elem)point = point.next_print("第", (index + 1), "個元素是:", point.elem)思考2:如何確定循環結束的條件?
定位元素–按下標定位
算法描述:
代碼實現:
def index(self, index_):'''定位元素--按下標定位'''if index_ >= self.length() and index_ < 0:raise LinkedListUnderflow("IndexError: list index out of range")count = 0point = self._headwhile count != index_:count += 1point = point.next_return point.elem定位元素–按值定位
算法描述:
代碼實現:
def find(self, val):'''定位元素--找值為x的元素'''point = self._headcount = 0# 此處需要判斷鏈表是否為空while point.elem != val and point != None:count += 1point = point.next_return count反轉鏈表
算法描述:修改鏈表中節點的關系
單鏈表反轉操作
注意:一定要將尾節點(反轉前的首節點)的next_(鏈接域)設為None,否則會出現死循環!!!
代碼實現:
def rev(self):'''反轉鏈表'''point = self._headself._head = self._head.next_# 必須要將原先的首節點的next_設為0,否則將在遍歷時出現死循環point.next_ = Nonewhile self._head != None:prepoint = pointpoint = self._headself._head = self._head.next_point.next_ = prepointself._head = point插入
首端插入
算法步驟:
思考3:能否交換上述算法步驟2、3
算法實現:
def prepend(self, elem):'''首端插入元素如果為空鏈表,則直接新建節點;如果為非空鏈表,則改變head指針和新節點的next_值'''n = Node(elem)if self._head == None:self._head = nelse:n.next_ = self._headself._head = n'''進階代碼1:n = Node(elem)n.next_ = self._headself._head = n進階代碼2:self._head = Node(elem, self._head)'''尾端插入
算法步驟:
代碼實現:
def append(self, elem):'''尾端插入元素'''n = Node(elem)if self._head == None: # 此種情況容易疏漏self._head = nelse:point = self._headwhile point.next_ != None:point = point.next_point.next_ = n定位插入
算法步驟:
代碼實現:
def insert(self, index, elem):'''任意位置插入元素'''n = Node(elem)if index >= self.length() and index < 0:raise LinkedListUnderflow('insert 下標越界,不合法', i)elif index == 0:self.prepend(elem)elif index == self.length():self.append(elem)else:count = 1point = self._headwhile count != index:count += 1point = point.next_n.next_ = point.next_point.next_ = n # =================================== # 方法二:簡化def insert(self, elem, index): # 在下標為i的元素之前插入elemn = LNode(elem)if index < 0 or index >= self.length():raise LinkedListUnderflow('insert 下標越界,不合法', index)elif index == 0:self.prepend(elem)else:point = self._headwhile index != 1:index -= 1point = point.next_n.next_ = point.next_point.next_ = n刪除
首部刪除
算法步驟:
單鏈表首端刪除操作
代碼實現:
def pop(self):'''首端刪除并返回被刪元素'''if self._head == None:raise LinkedListUnderflow("in pop")else:n = self._head.elemself._head = self._head.next_return n尾部刪除
算法步驟:
代碼實現:
def poplast(self):'''尾端刪除并返回刪除的值'''# 空鏈表if self._head == None:raise LinkedListUnderflow("in pop last")# 只有一個元素elif self._head.next_ == None:e = self._head.elemself._head.next_ = Nonereturn e# 鏈表中有兩個或多個元素else:point = self._headwhile point.next_.next_ != None:point = point.next_e = point.next_.elempoint.next_ = Nonereturn e定位刪除
算法描述:
代碼實現:
def remove(self, index):'''定位刪除'''if self._head == None:raise LinkedListUnderflow("鏈表為空")if index < 0 and index >= self.length():raise LinkedListUnderflow('remove 下標越界,不合法', i)elif index == 0:self.pop()elif index == self.length() - 1:self.poplast()else:count = 1point = self._headwhile count != index:count += 1point = point.next_e = point.next_point.next_ = point.next_.next_return e # ========================================== # 方法二:簡化def remove(self, index): #刪除下標為index的元素if self._head == None:raise LinkedListUnderflow("鏈表為空")if index < 0 or index >= self.length():raise LinkedListUnderflow('remove 下標越界,不合法', index)elif index == 0:self.pop()else:point = self._headwhile index != 1:index -= 1point = point.next_e = point.next_point.next_ = point.next_.next_return e思考與解答
思考1
為什么在遍歷鏈表時需要新建一個point變量,而不是直接用self._head去遍歷?
因為self._head是首指針,首指針的移動會使得鏈表節點發生變化。如下圖所以,當首指針移動到第三個節點,因為第1、2個節點沒有被引用,所以這兩個節點會被Python的內存管理機制清除(刪除節點)
思考2
在遍歷鏈表或訪問特定的節點時,如何確定循環結束的條件?
依據與目標節點相鄰的特殊節點的特點來設置循環條件。在遍歷操作中,我們需要遍歷到最后一個節點,最后一個節點本身就是一個特殊節點,其特點是next_(鏈接域)為None;如果需要遍歷到倒數第二個節點,同樣借助最后一個節點的特殊性。
思考3
在鏈表中插入元素時能否先將首節點或上一節點的指針先指向新節點?
不能,如果將首節點或上一節點的指針指向新節點,那么后面的節點會失去引用,即被刪除
參考文章:
總結
以上是生活随笔為你收集整理的实现单链表--Python的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java中导入错误的jar所引发的问题
- 下一篇: 微信小程序 wx:for