程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

數據結構Python版(二)——鏈表

編輯:Python

目錄

  • 一、什麼是鏈表?
  • 二、單鏈表
    • 2.1 鏈表結點類與鏈表類
    • 2.2 從數組中建立單鏈表
      • 2.2.1 頭插法
      • 2.2.2 尾插法
    • 2.3 線性表的運算在單鏈表中的實現
      • 2.3.1 查找序號為 i i i 的結點
      • 2.3.2 將元素添加到單鏈表的末尾
      • 2.3.3 插入元素
      • 2.3.4 刪除元素
      • 2.3.5 獲取元素
      • 2.3.6 設置元素
      • 2.3.7 查找第一個為e的序號
      • 2.3.8 獲取單鏈表長度
      • 2.3.9 打印單鏈表
  • 三、雙鏈表
    • 3.1 鏈表結點類與鏈表類
    • 3.2 從數組中建立雙鏈表
      • 3.2.1 頭插法
      • 3.2.2 尾插法
    • 3.3 雙鏈表的一些實現
      • 3.3.1 插入元素
      • 3.3.2 刪除元素
  • 四、循環鏈表
    • 4.1 循環單鏈表
    • 4.2 循環雙鏈表
  • 五、順序表與鏈表的比較
    • 5.1 基於空間的考慮
    • 5.2 基於時間的考慮
  • 六、LeetCode實戰
    • 6.1 LeetCode#27——移除元素
      • 方法一:雙指針
      • 方法二:雙指針優化
    • 6.2 LeetCode#26——刪除排序數組中的重復項
    • 6.3 LeetCode#80——刪除排序數組中的重復項II
    • 6.4 LeetCode#4——尋找兩個有序數組的中位數
    • 6.5 LeetCode#21——合並兩個有序鏈表
      • 方法一:迭代
      • 方法二:遞歸
    • 6.6 LeetCode#83——刪除排序鏈表中的重復元素
    • 6.7 LeetCode#203——移除鏈表元素
    • 6.8 LeetCode#19——刪除鏈表的倒數第n個結點
    • 6.9 LeetCode#234——回文鏈表
    • 6.10 LeetCode#61——旋轉鏈表
  • 七、附錄
    • 7.1 單鏈表完整代碼
    • 7.2 雙鏈表完整代碼

一、什麼是鏈表?

線性表的鏈式存儲結構稱為鏈表。在鏈表中每個結點不僅包含元素本身的信息,還包含元素之間邏輯關系的信息,即一個結點中包含後繼結點的地址信息或者前驅結點的地址信息,稱為指針屬性。這樣將可以通過一個結點的指針屬性方便地找到後繼結點或前驅結點。

如果每個結點只設置一個指向其後繼結點的指針屬性,這樣的鏈表稱為單(向)鏈表。如果每個結點設置兩個指針屬性,分別用於指向其前驅結點和後繼結點,這樣的鏈表稱為雙(向)鏈表。如下圖所示:

無前驅結點或者後繼結點的相應指針屬性用 None 表示。

為了便於在鏈表中插入和刪除結點,通常鏈表帶有一個頭結點,並通過頭結點指針唯一標識該鏈表。

通常將 p p p 指向的結點稱為 p p p 結點或結點 p p p,頭結點為 head 的鏈表稱為 head 鏈表。頭結點中不存放任何數據元素(空表是僅包含頭結點的鏈表)。一般鏈表的長度不計頭結點,僅指其中數據結點的個數。

二、單鏈表

2.1 鏈表結點類與鏈表類

為實現單鏈表,我們需要創建鏈表結點類與鏈表類,如下:

class LinkNode:
def __init__(self, data=None):
self.data = data # 存放數據,默認為None,即沒有數據
self.next = None # 指針,用於指向下一個結點
class LinkList:
def __init__(self):
self.head = LinkNode() # 頭結點不包含數據,因此不傳入參數
self.head.next = None # 初始化鏈表時,得到的是空鏈表

2.2 從數組中建立單鏈表

2.2.1 頭插法

該方法從一個空表開始,不斷讀取數組 a a a 中的元素並生成新結點 s s s,再將 s s s 插入到鏈表的表頭處。

def fromlist(self, a):
for i in range(len(a)):
s = LinkNode(a[i])
s.next = self.head.next
self.head.next = s

時間復雜度 O ( n ) \mathcal{O}(n) O(n), n n n 為 a a a 中元素的個數。

顯而易見,采用頭插法建立出來的鏈表,其數據次序與 a a a 中的次序完全相反。

2.2.2 尾插法

若希望鏈表中的數據次序與原數組保持一致,可采用尾插法。為此需要增加一個尾指針 t t t,使其始終指向當前鏈表的尾結點。

def fromlist(self, a):
t = self.head
for i in range(len(a)):
s = LinkNode(a[i])
t.next = s
t = s
t.next = None

時間復雜度 O ( n ) \mathcal{O}(n) O(n)。

2.3 線性表的運算在單鏈表中的實現

為在單鏈表中實現線性表的相關運算,我們首先需要實現一個功能,即查找序號為 i i i 的結點。

2.3.1 查找序號為 i i i 的結點

def geti(self, i):
p = self.head # 從頭結點開始遍歷
j = -1 # 因為頭結點指向的下一個結點的索引是0
while j < i and p is not None:
p = p.next
j += 1
return p

時間復雜度 O ( n ) \mathcal{O}(n) O(n)。

2.3.2 將元素添加到單鏈表的末尾

def add(self, e):
p = self.head
s = LinkNode(e)
while p.next is not None:
p = p.next
p.next = s

時間復雜度 O ( n ) \mathcal{O}(n) O(n)。

2.3.3 插入元素

插入元素遵循以下步驟:

def insert(self, i, e):
assert i >= 0
p = self.geti(i - 1)
assert p is not None
s = LinkNode(e)
s.next = p.next
p.next = s

時間復雜度 O ( n ) \mathcal{O}(n) O(n)。

2.3.4 刪除元素

刪除元素遵循以下步驟:

def delete(self, i):
assert i >= 0
p = self.geti(i - 1)
assert p is not None and p.next is not None
p.next = p.next.next

時間復雜度 O ( n ) \mathcal{O}(n) O(n)。

2.3.5 獲取元素

def __getitem__(self, i):
assert i >= 0
p = self.geti(i)
assert p is not None
return p.data

時間復雜度 O ( n ) \mathcal{O}(n) O(n)。

2.3.6 設置元素

def __setitem__(self, i, x):
assert i >= 0
p = self.geti(i)
assert p is not None
p.data = x

時間復雜度 O ( n ) \mathcal{O}(n) O(n)。

2.3.7 查找第一個為e的序號

def getno(self, e):
p = self.head.next
k = 0
while p is not None and p.data != e:
p = p.next
k += 1
return -1 if p is None else k # 未找到時返回-1

時間復雜度 O ( n ) \mathcal{O}(n) O(n)。

2.3.8 獲取單鏈表長度

def getsize(self):
p = self.head.next
j = 1
while p.next is not None:
p = p.next
j += 1
return j

時間復雜度 O ( n ) \mathcal{O}(n) O(n)。

2.3.9 打印單鏈表

打印單鏈表即打印其中的所有數據元素。

def display(self):
p = self.head.next
while p.next is not None:
print(p.data, end=' ')
p = p.next
print(p.data)

這種打印方式可保證最後一個數字後面的字符不是空格而是換行符。

三、雙鏈表

3.1 鏈表結點類與鏈表類

與單鏈表類似:

class DLinkNode:
def __init__(self, data=None):
self.data = data
self.prior = None
self.next = None
class DLinkList:
def __init__(self):
self.dhead = DLinkNode()
self.dhead.prior = None
self.dhead.next = None

和單鏈表一樣,雙鏈表也是由頭結點 head 唯一標識的。

3.2 從數組中建立雙鏈表

3.2.1 頭插法

def fromlist(self, a):
for i in range(len(a)):
s = DLinkList(a[i])
# 先建立s與其下一個結點的雙向關系
s.next = self.dhead.next
if self.dhead.next is not None:
self.dhead.next.prior = s
# 再建立s與其上一個結點的雙向關系
self.dhead.next = s
s.prior = self.dhead

3.2.2 尾插法

def fromlist(self, a):
t = self.dhead
for i in range(len(a)):
s = DLinkList(a[i])
# 建立s與鏈表尾結點的雙向關系
t.next = s
s.prior = t
# 移動指針
t = s
t.next = None

3.3 雙鏈表的一些實現

3.3.1 插入元素

與單鏈表類似:

def insert(self, i, e):
assert i >= 0
p = self.geti(i - 1)
assert p is not None
s = DLinkNode(e)
s.next = p.next
if p.next is not None:
p.next.prior = s
p.next = s
s.prior = p

3.3.2 刪除元素

與單鏈表類似:

def delete(self, i):
assert i >= 0
p = self.geti(i)
assert p is not None
p.prior.next = p.next
if p.next is not None:
p.next.prior = p.prior

四、循環鏈表

4.1 循環單鏈表

循環單鏈表的尾結點的指針屬性不再是 None,而是指向頭結點:

循環單鏈表的特點是從表中任一結點出發都可以找到其他結點。與單鏈表相比,無需增加存儲空間,僅對鏈接方式稍作修改,即可使得表處理更加方便、靈活。

循環單鏈表中的結點類型與非循環單鏈表中的結點類型相同,仍為 LinkNode

class CLinkList:
def __init__(self):
self.head = LinkNode()
self.head.next = self.head # 因為初始時鏈表是空的,所以頭結點指向頭結點

循環單鏈表的插入和刪除結點操作與非循環單鏈表的相同,所以兩者的許多基本運算算法是相似的,主要區別如下:

  • 初始只有頭結點 head,在循環單鏈表的構造方法中需要通過 head.next = head 語句設置空表;
  • 循環單鏈表中涉及查找操作時需要修改表尾判斷的條件。例如使用 p p p 遍歷時,尾結點滿足的條件是 p.next == head 而不是 p.next == None

4.2 循環雙鏈表

循環雙鏈表的尾結點的 next 指針指向頭結點,頭結點的 prior 指針指向尾結點:

其特點是整個鏈表形成兩個環,由此從表中任一結點出發均可找到其他結點,最突出的優點是通過頭結點在 O ( 1 ) \mathcal{O}(1) O(1) 時間內找到尾結點。

循環雙鏈表中的結點類型與非循環雙鏈表中的結點類型相同,仍為 DLinkNode

class CDLinkList:
def __init__(self):
self.dhead = DLinkNode()
self.dhead.next = self.dhead
self.dhead.prior = self.dhead

五、順序表與鏈表的比較

5.1 基於空間的考慮

對於一種存儲結構,定義其存儲密度:
存 儲 密 度 = 結點中數據本身占用的存儲量 整個結點占用的存儲量 存儲密度=\frac{\text{結點中數據本身占用的存儲量}}{\text{整個結點占用的存儲量}} 存儲密度=整個結點占用的存儲量結點中數據本身占用的存儲量​
一般地,存儲密度越大,存儲空間的利用率就越高。

顯然,順序表的存儲密度為1,而鏈表的存儲密度小於1。僅從存儲密度看,順序表的存儲空間利用率更高

此外,順序表需要預先分配空間,如果空間過小會出現上溢出,如果空間過大會導致內存浪費。而鏈表的存儲空間是動態分配的,只要內存足夠,就不會出現上溢出。

所以,

  • 當線性表的長度變化不大,易於事先確定時,宜采用順序表。
  • 當線性表的長度變化較大,難以估計其存儲大小時,宜采用鏈表。

5.2 基於時間的考慮

從查找操作來看,順序表具有隨機存儲特性,給定序號查找指定元素的時間為 O ( 1 ) \mathcal{O}(1) O(1);而鏈表不具有隨機存儲特性,只能順序訪問,給定序號查找指定元素的時間為 O ( n ) \mathcal{O}(n) O(n)。

從插入和刪除操作來看,在順序表中執行該操作通常需要移動半個表的元素;而在鏈表中執行該操作僅需要修改相關結點的指針屬性,不必移動結點。

所以,

  • 如果線性表的主要運算是查找,宜采用順序表;
  • 如果線性表的主要運算是插入和刪除,宜采用鏈表;

六、LeetCode實戰

LeetCode中的單鏈表結點定義為:

class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next

以下所有代碼均通過測試

6.1 LeetCode#27——移除元素


本題LeetCode會自動追蹤 nums,因此只需返回整數。

方法一:雙指針

我們可以設置兩個指針:leftright。其中右指針指向當前要處理的元素,左指針指向下一個將要賦值的位置。

如果右指針指向的元素等於 val,則此時左指針不動,右指針右移一位。否則,將右指針指向的元素復制到左指針的位置,然後將左右指針同時右移一位。

class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
left = 0
for right in range(len(nums)):
if nums[right] != val:
nums[left] = nums[right]
left += 1
return left

注意到該算法在最壞的情況下,即 nums 中不含 val,左右指針均要遍歷一次數組,即總共遍歷了兩次。此外,該算法會對要保留的元素進行重復賦值(當 left = right 時)。

復雜度分析

  • 時間復雜度: O ( n ) \mathcal{O}(n) O(n),其中 n n n 為序列的長度。我們只需要遍歷該序列至多兩次。
  • 空間復雜度: O ( 1 ) \mathcal{O}(1) O(1)。我們只需要常數的空間保存若干變量。

方法二:雙指針優化

考慮初始時將左右指針分別置於數組的兩端。

當左指針指向 val 時,此時將右指針指向的元素復制到左指針處,同時將右指針左移一位。如果復制過來的元素恰好也是 val,則繼續執行上述操作直至左指針指向的不是 val

當左指針指向的不是 val 時,將左指針右移一位。

當左右指針重合時,左右指針一起遍歷了整個數組。

class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
if nums[left] == val:
nums[left] = nums[right]
right -= 1
else:
left += 1
return left

復雜度分析

  • 時間復雜度: O ( n ) \mathcal{O}(n) O(n),其中 n n n 為序列的長度。我們只需要遍歷該序列至多一次。
  • 空間復雜度: O ( 1 ) \mathcal{O}(1) O(1)。我們只需要常數的空間保存若干變量。

6.2 LeetCode#26——刪除排序數組中的重復項


從有序可以得知,重復項一定是連在一起的。此外,由於本題未給定 val,所以可能會有多種重復元。

具體來說,設置兩個左右指針,初始時均位於數組左端。當右指針指向的元素和左指針相同時,右指針右移一位。若不同,則先將左指針右移一位,再將右指針指向的值復制到左指針處。

class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
left = 0
for right in range(len(nums)):
if nums[left] != nums[right]:
left += 1
nums[left] = nums[right]
return left + 1

6.3 LeetCode#80——刪除排序數組中的重復項II


我們采用快慢指針解法。其中慢指針表示處理出的數組的長度,快指針表示已經檢查過的數組的長度。

初始時置快慢指針均在數組左端,且應保留數組的前兩個數字。因此起初應讓快慢指針同步移動到索引 2 2 2 處且不做任何修改。因為同一元素最多出現兩次,所以比較 nums[slow - 2]nums[fast],當它們相同時,只讓快指針右移一位;當它們不同時,將快指針指向的值復制到左指針處,然後讓左右指針均右移一位。

class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
slow = 0
for fast in range(len(nums)):
if slow < 2 or nums[slow - 2] != nums[fast]:
nums[slow] = nums[fast]
slow += 1
return slow

6.4 LeetCode#4——尋找兩個有序數組的中位數



擺爛解法:

class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
nums = sorted(nums1 + nums2)
n = len(nums)
if n % 2 != 0:
return nums[n // 2]
else:
return (nums[n // 2 - 1] + nums[n // 2]) / 2

因為調用了 sorted 函數,所以時間復雜度是 O ( ( m + n ) log ⁡ ( m + n ) ) \mathcal{O}((m+n)\log(m+n)) O((m+n)log(m+n))。所以雖然上述代碼能夠成功運行,但並不符合題目要求。

6.5 LeetCode#21——合並兩個有序鏈表


方法一:迭代

首先設定一個哨兵結點 sentinel,這可以在最後讓我們比較容易地返回合並後的鏈表。我們設置三個指針 l1l2prevl1l2 分別遍歷 list1list2。初始時 prev 指向哨兵結點。

l1 小於 l2 時,我們將 l1 指向的結點接在 prev 後,然後將 prevl1 均後移一位。否則,我們將 l2 指向的結點接在 prev 後,然後將 prevl2 均後移一位。

循環終止的時候,l1l2 至多有一個是非空的,由於輸入的兩個鏈表都是有序的,所以我們只需要簡單地將非空鏈表接在合並鏈表的後面,並返回合並鏈表即可。

class Solution:
def mergeTwoLists(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
sentinel = ListNode(-1)
prev = sentinel
while l1 and l2:
if l1.val < l2.val:
prev.next = l1
l1 = l1.next
else:
prev.next = l2
l2 = l2.next
prev = prev.next
prev.next = l2 if l1 is None else l1
return sentinel.next

時間復雜度: O ( m + n ) \mathcal{O}(m+n) O(m+n),空間復雜度: O ( 1 ) \mathcal{O}(1) O(1)。

方法二:遞歸

class Solution:
def mergeTwoLists(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
# 邊界條件
if l1 is None:
return l2
if l2 is None:
return l1
# 遞歸主體
if l1.val < l2.val:
l1.next = self.mergeTwoLists(l1.next, l2)
return l1
else:
l2.next = self.mergeTwoLists(l1, l2.next)
return l2

時間復雜度: O ( m + n ) \mathcal{O}(m+n) O(m+n),空間復雜度: O ( m + n ) \mathcal{O}(m+n) O(m+n)。

遞歸調用 mergeTwoLists 函數時需要消耗棧空間,棧空間的大小取決於遞歸調用的深度。結束遞歸調用時 mergeTwoLists 函數最多調用 n + m n+m n+m 次,因此空間復雜度為 O ( n + m ) \mathcal{O}(n+m) O(n+m)。

6.6 LeetCode#83——刪除排序鏈表中的重復元素

class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if not head:
return head
cur = head
while cur.next:
if cur.val == cur.next.val:
cur.next = cur.next.next
else:
cur = cur.next
return head

6.7 LeetCode#203——移除鏈表元素


因為頭結點可能會變動,所以我們依然使用哨兵結點的方法。此外,設置一個指針 cur 用來遍歷整個列表。

class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
if not head: return head
sentinel = ListNode(-1)
sentinel.next = head
cur = sentinel
while cur.next:
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return sentinel.next

本題依然可以采用遞歸的方法。

class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
if not head: return head
head.next = self.removeElements(head.next, val)
# 如果head本身就需要刪除,則返回head的下一個結點,否則返回head本身
return head.next if head.val == val else head

6.8 LeetCode#19——刪除鏈表的倒數第n個結點

首先最容易想到的方法就是兩次遍歷。第一次遍歷計算鏈表的總長度,第二次遍歷確定要刪除的結點。該方法過於簡單粗暴所以這裡不再介紹。

對於本題,我們可以設置一個快慢指針,想辦法讓快指針走到鏈表末尾時,慢指針剛好位於倒數第 n + 1 n+1 n+1 個結點。

class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
sentinel = ListNode(-1, head)
slow, fast = sentinel, head
for _ in range(n):
fast = fast.next
while fast:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return sentinel.next

6.9 LeetCode#234——回文鏈表

最粗暴的方法就是將鏈表的值依次存儲到一個列表 nums 中,然後通過 nums==nums[::-1] 來判斷所給鏈表是不是回文鏈表。

另外一種思路是將鏈表的前半部分依次壓入棧中,然後再將棧中元素依次彈出並與鏈表的後半部分比較。此方法需要考慮鏈表長度的奇偶性。

以上兩種方法的空間復雜度均為 O ( n ) \mathcal{O}(n) O(n),下面提供一種 O ( 1 ) \mathcal{O}(1) O(1) 的解決方案——快慢指針法。

整個流程分為以下幾個步驟:

  • 找到前半部分鏈表的尾節點。可用快慢指針,慢指針一次移動一位,快指針一次移動兩位。
  • 反轉後半部分鏈表。
  • 設置左右指針,分別位於前半部分的起始處和後半部分的起始處,然後逐步右移來判斷是否回文。
  • 返回結果。
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if not head: return True
first_half_end = self.get_middle_position(head)
second_half_start = self.reverse_list(first_half_end.next)
flag = True
left, right = head, second_half_start
while flag and right:
if left.val != right.val:
flag = False
left = left.next
right = right.next
return flag
def get_middle_position(self, head):
slow, fast = head, head
while fast.next and fast.next.next:
slow = slow.next
fast = fast.next.next
return slow
def reverse_list(self, head):
prev, cur = None, head
while cur:
next_node = cur.next
cur.next = prev
prev = cur
cur = next_node
return prev

嚴謹來講,我們還應在判斷完後恢復鏈表。

6.10 LeetCode#61——旋轉鏈表


可先將鏈表構成循環鏈表,然後再將指定的位置切開。

class Solution:
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if not head: return head
# 構成循環鏈表並計算鏈表長度
p = head
length = 1
while p.next:
p = p.next
length += 1
p.next = head
# 斷開鏈接
pos = length - k % length
p = head
k = 1
while k < pos:
p = p.next
k += 1
next_node = p.next
p.next = None
return next_node

七、附錄

7.1 單鏈表完整代碼

class LinkNode:
def __init__(self, data=None):
self.data = data
self.next = None
class LinkList:
def __init__(self):
self.head = LinkNode()
self.head.next = None
def fromlist(self, a):
t = self.head
for i in range(len(a)):
s = LinkNode(a[i])
t.next = s
t = s
t.next = None
def geti(self, i):
p = self.head
j = -1
while j < i and p is not None:
p = p.next
j += 1
return p
def add(self, e):
p = self.head
s = LinkNode(e)
while p.next is not None:
p = p.next
p.next = s
def insert(self, i, e):
assert i >= 0
p = self.geti(i - 1)
assert p is not None
s = LinkNode(e)
s.next = p.next
p.next = s
def delete(self, i):
assert i >= 0
p = self.geti(i - 1)
assert p is not None and p.next is not None
p.next = p.next.next
def __getitem__(self, i):
assert i >= 0
p = self.geti(i)
assert p is not None
return p.data
def __setitem__(self, i, x):
assert i >= 0
p = self.geti(i)
assert p is not None
p.data = x
def getno(self, e):
p = self.head.next
k = 0
while p is not None and p.data != e:
p = p.next
k += 1
return -1 if p is None else k
def getsize(self):
p = self.head.next
j = 1
while p.next is not None:
p = p.next
j += 1
return j
def display(self):
p = self.head.next
while p.next is not None:
print(p.data, end=' ')
p = p.next
print(p.data)

7.2 雙鏈表完整代碼

class DLinkNode:
def __init__(self, data=None):
self.data = data
self.prior = None
self.next = None
class DLinkList:
def __init__(self):
self.dhead = DLinkNode()
self.dhead.prior = None
self.dhead.next = None
def fromlist(self, a):
t = self.dhead
for i in range(len(a)):
s = DLinkList(a[i])
# 建立s與鏈表尾結點的雙向關系
t.next = s
s.prior = t
# 移動指針
t = s
t.next = None
def geti(self, i):
p = self.dhead
j = -1
while j < i and p is not None:
p = p.next
j += 1
return p
def insert(self, i, e):
assert i >= 0
p = self.geti(i - 1)
assert p is not None
s = DLinkNode(e)
s.next = p.next
if p.next is not None:
p.next.prior = s
p.next = s
s.prior = p
def delete(self, i):
assert i >= 0
p = self.geti(i)
assert p is not None
p.prior.next = p.next
if p.next is not None:
p.next.prior = p.prior

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved