[Python] 數據結構--實現順序表、鏈表、棧和隊列

說明:

  本文主要展示Python實現的幾種常用數據結構:順序表、鏈表、棧和隊列。

  附有實現代碼。

  來源主要參考網絡文章。

 

一、順序表

  1、順序表的結構

    一個順序表的完整信息包括兩部分,一部分是表中元素集合,另一部分是爲實現正確操作而需記錄的信息,即有關表的整體情況的信息,這部分信息主要包括元素存儲區的容量和當前表中已有的元素個數兩項。

    

  

  2、順序表的兩種基本實現方式

    

 

    圖a 爲一體式結構,存儲表信息的單元與元素存儲區以連續的方式安排在一塊存儲區裏,兩部分數據的整體形成一個完整的順序表對象。一體式結構整體性強,易於管理。但是由於數據元素存儲區域是表對象的一部分,順序表創建後,元素存儲區就固定了。

    圖b 爲分離式結構,表對象裏只保存與整個表有關的信息(容量和元素個數),實際數據元素存放在另一個獨立的元素存儲區裏,通過鏈接與基本表對象關聯。

  

  3、元素存儲區替換

    一體式結構由於順序表信息區與數據區聯繫存儲在一起,所以若想更換數據區,則只能整體搬遷,即整個順序表對象(指存儲順序表的結構信息的區域)改變了。

    分離式結構若想更換數據區,只需將表信息區中的數據區鏈接地址更新即可,而該順序表對象不變。

 

  4、元素存儲區擴充及其策略

    分離式結構的順序表,如想將數據區更換爲存儲空間更大的區域,則可以在不改變表對象的前提下對其數據存儲區進行了擴充,所有使用這個表的地方都不必修改。只要程序的運行環境(計算機系統)還有空閒存儲,這種表結構就不會因爲滿了而導致操作無法進行。人們把採用這種技術實現的順序表稱爲動態順序表,因爲其容量可以在使用中動態變化。

    擴充的兩種策略:

      》每次擴充增加固定數目的存儲位置,如每次擴充10個元素位置,這種策略可稱爲線性增長。

        (特點:節省空間,但是擴充操作頻繁,操作次數多)

      》每次擴充容量加倍,如每次擴充增加一倍存儲空間。

        (特點:減少了擴充操作的執行次數,但可能會浪費空間資源。以空間換時間,推薦的方式)

      》Python的官方實現中,list 實現採用瞭如下的策略:在建立空表(或很小的表)時,系統分配一塊能容納8個元素的存儲區;在執行插入操作(insert或append)時,如果元素存儲區滿就換一塊4倍大的存儲區。但如果此時的表已經很大(目前閥值爲50000),則改變策略,採用加一倍的方法。引入這種改變策略的方式,是爲了避免出現過多的空閒的存儲位置。

 

  5、順序表的操作

    增加元素,下圖爲順序表增加元素的三種方式:

      

      a、尾端加入元素,時間複雜度爲 O(1)

      b、非保序的加入元素(不常見)沒時間複雜度爲 O(1)

      c、保序的元素加入,時間複雜度爲 O(n)

      

    刪除元素,下圖爲順序表刪除元素的三種方式:

      

      a、刪除表尾元素,時間複雜度爲 O(1)

      b、非保序的元素刪除(不常見),時間複雜度爲 O(1)

      c、保序的元素刪除,時間複雜度爲 O(n)

      

 

  6、Python 中的順序表

    Python中的 list 和 tuple 兩種類型採用了順序表的實現技術,具有前面討論的順序表的所有性質。

    tuple是不可變類型,即不變的順序表,因此不支持改變其內部狀態任何操作,而其他方面,則與list的性質類似。

    list的基本實現技術:

      Python表中類型list就是一種元素個數可變的線性表,可以加入和刪除元素,並在各種操作維持已有元素順序(即保序),而且還具有以下行爲特徵:

        》基於下標(位置)的高效元素訪問和更新,時間複雜度應該是 O(1);

          爲滿足該特徵,應該採用順序表技術,表中元素保存在一塊連續的存儲區中。

        》允許任意加入元素,而且在不斷加入元素的過程中,表對象的標識(函數id得到的值)不變

          爲滿足該特徵,就必須能更換元素存儲區,並且爲保證更換存儲區時list對象的標識id不變,只能採用分離式實現技術

 

      在Python官方實現中,list就是一種採用分離式技術實現的動態順序表。這就是爲什麼用list.append(x)(或list.insert(len(list), x), 即尾部插入)比在指定位置插入元素效率高的原因。

 

二、鏈表

  相對於順序表,鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理,因爲順序表的結構需要預先知道數據大小來申請連續的存儲空間,而在進行擴充時又需要進行數據的搬遷。

  鏈表(Linked list)是一種常見的基礎數據結構,是一種線性表,但是不像順序表一樣連續存儲數據,而是每一個結點(數據存儲單元)裏存放下一個結點的信息(即地址):

    

  1、單向鏈表

    單向鏈表也叫單鏈表,是表中最簡單的一種形式,它的每個節點包含兩個域,一個信息域(元素域)和一個鏈接域。這個鏈接指向鏈表中的下一個節點,而最後一個節點的鏈接域則指向一個空值。

    

    表中元素elem用來存放具體的數據。

    鏈接域next用來存放下一個節點的位置(Python中的標識)。

    變量p指向鏈表的頭節點(首節點)的位置,從p出發能找到表中的任意節點。  

 

    單鏈表的操作:

      is_empty():鏈表是否爲空

      length():鏈表長度

      travel():遍歷整個鏈表

      add(item):鏈表頭部添加元素

      append(item):鏈表尾部添加元素

      insert(pos, item):指定位置添加元素

      remove(item):刪除節點

      search(item):查找節點是否存在

 

    代碼實現:

  1 # coding=utf-8
  2 # 單鏈表的實現
  3 
  4 
  5 class SingleNode:
  6     """單鏈表的節點"""
  7     def __init__(self, item):
  8         # item存放數據元素
  9         self.item = item
 10         # 下一個節點
 11         self.next = None
 12 
 13     def __str__(self):
 14         return str(self.item)
 15 
 16 
 17 class SingleLinkList:
 18     """單鏈表"""
 19     def __init__(self):
 20         self._head = None
 21 
 22     def is_empty(self):
 23         """判斷鏈表是否爲空"""
 24         return self._head is None
 25 
 26     def length(self):
 27         """獲取鏈表長度"""
 28         cur = self._head
 29         count = 0
 30         while cur is not None:
 31             count += 1
 32             # 將cur後移,指向下一個節點
 33             cur = cur.next
 34         return count
 35 
 36     def travel(self):
 37         """遍歷鏈表"""
 38         cur = self._head
 39         while cur is not None:
 40             print(cur.item)
 41             cur = cur.next
 42         print("")
 43 
 44     def add(self, item):
 45         """鏈表頭部添加元素"""
 46         node = SingleNode(item)
 47 
 48         node.next = self._head
 49         self._head = node
 50 
 51     def append(self, item):
 52         """鏈表尾部添加元素"""
 53         node = SingleNode(item)
 54 
 55         if self.is_empty():
 56             self._head = node
 57         else:
 58             cur = self._head
 59             while cur.next is not None:
 60                 cur = cur.next
 61 
 62             # 此時cur指向鏈表最後一個節點,即 next = None
 63             cur.next = node
 64 
 65     def insert(self, pos, item):
 66         """指定位置添加元素"""
 67         # 若指定位置pos爲第一個元素之前,則執行頭部插入
 68         if pos <= 0:
 69             self.add(item)
 70 
 71         # 若指定位置超過鏈表尾部,則執行尾部插入
 72         elif pos > (self.length() - 1):
 73             self.append(item)
 74 
 75         # 找到指定位置
 76         else:
 77             node = SingleNode(item)
 78             cur = self._head
 79             cur_pos = 0
 80             while cur.next is not None:
 81                 # 獲取需要插入位置的上一個節點
 82                 if pos - 1 == cur_pos:
 83                     node.next = cur.next
 84                     cur.next = node
 85                 cur = cur.next
 86                 cur_pos += 1
 87 
 88     def remove(self, item):
 89         """刪除節點"""
 90         cur = self._head
 91         while cur is not None:
 92             if cur.next.item == item:
 93                 cur.next = cur.next.next
 94                 break
 95             cur = cur.next
 96 
 97     def search(self, item):
 98         """查找節點是否存在"""
 99         cur = self._head
100         count = 0
101         while cur is not None:
102             if cur.item == item:
103                 return count
104             cur = cur.next
105             count += 1
106 
107         # 找不到元素
108         if count == self.length():
109             count = -1
110         return count
111 
112 
113 if __name__ == "__main__":
114     ll = SingleLinkList()
115     ll.add(1)           # 1
116     ll.add(2)           # 2 1
117     ll.append(3)        # 2 1 3
118     ll.insert(2, 4)     # 2 1 4 3
119     print("length:", ll.length())   # 4
120     ll.travel()         # 2 1 4 3
121     print("search(3):", ll.search(3))   # 3
122     print("search(5):", ll.search(5))   # -1
123     ll.remove(1)
124     print("length:", ll.length())       # 3
125     ll.travel()         # 2 4 3

 

    鏈表與順序表的對比:

      鏈表失去了順序表隨機讀取的優點,同時鏈表由於增加了節點的指針域,空間開銷比較大,但對存儲空間的使用要相對靈活。

      鏈表與順序表的各種操作複雜度如下所示:

操作 鏈表 順序表
訪問元素 O(n) O(1)
在頭部插入/刪除 O(1) O(n)
在尾部安插入/刪除 O(n) O(1)
在中間插入/刪除 O(n) O(n)

      注意:雖然表面看起來複雜度都是 O(n),但是鏈表和順序表在插入和刪除時進行的是完全不同的操作。鏈表的主要耗時操作是遍歷查找,刪除和插入操作本身的複雜度是O(1)。順序表查找很快,主要耗時的操作是拷貝覆蓋。因爲除了目標元素在尾部的特殊情況,順序表進行插入和刪除時需要對操作點之後所有元素進行前後移位操作,只能通過拷貝和覆蓋方法進行。

  

  2、單向循環鏈表

    單鏈表的一個變形是單向循環鏈表,鏈表中最後一個節點的next域不再爲None,而是指向鏈表的頭結點。

    

    基本操作和單鏈表基本一樣,實現代碼如下:

  1 # coding=utf-8
  2 # 單向循環鏈表
  3 
  4 
  5 class Node:
  6     """節點"""
  7     def __init__(self, item):
  8         self.item = item
  9         self.next = None
 10 
 11     def __str__(self):
 12         return str(self.item)
 13 
 14 
 15 class SinCycLinkedList:
 16     """單向循環鏈表"""
 17     def __init__(self):
 18         self._head = None
 19 
 20     def is_empty(self):
 21         """判斷鏈表是否爲空"""
 22         return self._head is None
 23 
 24     def length(self):
 25         """鏈表長度"""
 26         if self.is_empty():
 27             return 0
 28         count = 1
 29         cur = self._head
 30         while cur.next != self._head:
 31             # print("cur", cur.item)
 32             count += 1
 33             cur = cur.next
 34         return count
 35 
 36     def travel(self):
 37         """遍歷"""
 38         if self.is_empty():
 39             return
 40 
 41         cur = self._head
 42         print(cur.item)
 43         while cur.next != self._head:
 44             cur = cur.next
 45             print(cur.item)
 46 
 47     def add(self, item):
 48         """在頭部添加一個節點"""
 49         node = Node(item)
 50         if self.is_empty():
 51             self._head = node
 52             node.next = self._head
 53         else:
 54             node.next = self._head
 55             cur = self._head
 56             while cur.next != self._head:
 57                 cur = cur.next
 58 
 59             cur.next = node
 60             self._head = node
 61 
 62     def append(self, item):
 63         """在尾部添加一個節點"""
 64         node = Node(item)
 65         if self.is_empty():
 66             self._head = node
 67             node.next = self._head
 68         else:
 69             cur = self._head
 70             # print(type(cur), cur.item, cur.next)
 71             while cur.next != self._head:
 72                 cur = cur.next
 73 
 74             # print(cur.item)
 75             cur.next = node
 76             node.next = self._head
 77 
 78     def insert(self, pos, item):
 79         """指定位置pos添加節點"""
 80         if pos <= 0:
 81             self.add(item)
 82         elif pos > (self.length() - 1):
 83             self.append(item)
 84         else:
 85             node = Node(item)
 86             cur = self._head
 87             cur_pos = 0
 88             while cur.next != self._head:
 89                 if (pos - 1) == cur_pos:
 90                     node.next = cur.next
 91                     cur.next = node
 92                     break
 93                 cur_pos += 1
 94                 cur = cur.next
 95 
 96     def remove(self, item):
 97         """刪除一個節點"""
 98         if self.is_empty():
 99             return
100 
101         pre = self._head
102         # 刪除首節點
103         if pre.item == item:
104             cur = pre
105             while cur.next != self._head:
106                 cur = cur.next
107 
108             cur.next = pre.next     # 刪除首節點(跳過該節點)
109             self._head = pre.next   # 重新指定首節點
110 
111         # 刪除其他的節點
112         else:
113             cur = pre
114             while cur.next != self._head:
115                 if cur.next.item == item:
116                     cur.next = cur.next.next
117                 cur = cur.next
118 
119     def search(self, item):
120         """查找節點是否存在"""
121         if self.is_empty():
122             return -1
123 
124         cur_pos = 0
125         cur = self._head
126         if cur.item == item:
127             return cur_pos
128 
129         while cur.next != self._head:
130             if cur.item == item:
131                 return cur_pos
132             cur_pos += 1
133             cur = cur.next
134 
135         if cur_pos == self.length() - 1:
136             return -1
137 
138 
139 if __name__ == "__main__":
140     ll = SinCycLinkedList()
141     ll.add(1)       # 1
142     ll.add(2)       # 2 1
143     # ll.travel()
144     ll.append(3)    # 2 1 3
145     ll.insert(2, 4) # 2 1 4 3
146     ll.insert(4, 5) # 2 1 4 3 5
147     ll.insert(0, 6) # 6 2 1 4 3 5
148     print("length:", ll.length())        # 6
149     ll.travel()                           # 6 2 1 4 3 5
150     print("search(3)", ll.search(3))     # 4
151     print("search(7)", ll.search(7))     # -1
152     print("search(6)", ll.search(6))    # 0
153     print("remove(1)")
154     ll.remove(1)
155     print("length:", ll.length())       # 6 2 4 3 5
156     print("remove(6)")
157     ll.remove(6)
158     ll.travel()

  

  3、雙向鏈表

    一種更復雜的鏈表是 "雙向鏈表" 或 "雙面鏈表"。每個節點有兩個鏈接:一個指向前一個節點,當次節點爲第一個節點時,指向空值;而另一個指向下一個節點,當此節點爲最後一個節點時,指向空值。

  

    

    基本操作和單鏈表一樣,不同的實現,代碼如下:

    

  1 # coding=utf-8
  2 # 雙向鏈表
  3 
  4 
  5 class Node:
  6     """節點"""
  7     def __init__(self, item):
  8         self.item = item
  9         self.prev = None
 10         self.next = None
 11 
 12 
 13 class DLinkList:
 14     """雙向鏈表"""
 15     def __init__(self):
 16         self._head = None
 17 
 18     def is_empty(self):
 19         """判斷鏈表是否爲空"""
 20         return self._head is None
 21 
 22     def length(self):
 23         """獲取鏈表長度"""
 24         if self.is_empty():
 25             return 0
 26         else:
 27             cur = self._head
 28             count = 1
 29             while cur.next is not None:
 30                 count += 1
 31                 cur = cur.next
 32 
 33             return count
 34 
 35     def travel(self):
 36         """遍歷鏈表"""
 37         print("↓↓" * 10)
 38         if self.is_empty():
 39             print("")
 40 
 41         else:
 42             cur = self._head
 43             print(cur.item)
 44             while cur.next is not None:
 45                 cur = cur.next
 46                 print(cur.item)
 47         print("↑↑" * 10)
 48 
 49     def add(self, item):
 50         """鏈表頭部添加節點"""
 51         node = Node(item)
 52         if self.is_empty():
 53             self._head = node
 54         else:
 55             cur = self._head
 56 
 57             node.next = cur
 58             cur.prev = node
 59             self._head = node
 60 
 61     def append(self, item):
 62         """鏈表尾部添加節點"""
 63         node = Node(item)
 64         if self.is_empty():
 65             self._head = node
 66         else:
 67             cur = self._head
 68             # 遍歷找到最後一個節點
 69             while cur.next is not None:
 70                 cur = cur.next
 71 
 72             # 在尾節點添加新的節點
 73             cur.next = node
 74             node.prev = cur
 75 
 76     def insert(self, pos, item):
 77         """指定位置添加"""
 78         # 頭部添加
 79         if pos <= 0:
 80             self.add(item)
 81 
 82         # 尾部添加
 83         elif pos > (self.length() - 1):
 84             self.append(item)
 85 
 86         # 其他位置添加
 87         else:
 88             node = Node(item)
 89 
 90             cur = self._head
 91             cur_pos = 0
 92             while cur.next is not None:
 93                 if cur_pos == (pos - 1):
 94                     # 與下一個節點互相指向
 95                     node.next = cur.next
 96                     cur.next.prev = node
 97                     # 與上一個節點互相指向
 98                     cur.next = node
 99                     node.prev = cur
100                 cur_pos += 1
101                 cur = cur.next
102 
103     def remove(self, item):
104         """刪除節點"""
105         if self.is_empty():
106             return
107         else:
108             cur = self._head
109             # 刪除首節點
110             if cur.item == item:
111                 self._head = cur.next
112                 cur.next.prev = None
113 
114             # 刪除其他節點
115             else:
116                 while cur.next is not None:
117                     if cur.item == item:
118                         # 刪除之前:1 ←→ [2] ←→ 3
119                         # 刪除之後:1 ←→ 3
120                         cur.prev.next = cur.next
121                         cur.next.prev = cur.prev
122                     cur = cur.next
123 
124                 # 刪除尾節點
125                 if cur.item == item:
126                     cur.prev.next = None
127 
128 
129     def search(self, item):
130         """查找節點是否存在"""
131         if self.is_empty():
132             return -1
133         else:
134             cur = self._head
135             cur_pos = 0
136             while cur.next is not None:
137                 if cur.item == item:
138                     return cur_pos
139 
140                 cur_pos += 1
141                 cur = cur.next
142 
143             if cur_pos == (self.length() - 1):
144                 return -1
145 
146 
147 if __name__ == "__main__":
148     ll = DLinkList()
149     ll.add(1)       # 1
150     ll.add(2)       # 2 1
151     ll.append(3)    # 2 1 3
152     ll.insert(2, 4) # 2 1 4 3
153     ll.insert(4, 5) # 2 1 4 3 5
154     ll.insert(0, 6) # 6 2 1 4 3 5
155     print("length:", ll.length())   # 6
156     ll.travel()                 # 6 2 1 4 3 5
157     print("search(3)", ll.search(3))
158     print("search(4)", ll.search(4))
159     print("search(10)", ll.search(10))
160     ll.remove(1)
161     print("length:", ll.length())
162     ll.travel()
163     print("刪除首節點 remove(6):")
164     ll.remove(6)
165     ll.travel()
166     print("刪除尾節點 remove(5):")
167     ll.remove(5)
168     ll.travel()

 

 

 

三、棧

  棧(stack),也稱爲堆棧,是一種容器,可存入數據元素、訪問元素、刪除元素,它的特點在於只能允許在容器的一端(稱爲棧頂端指標:top)進行加入數據(push)和輸出數據(pop)的運算。沒有了位置概念,保證任何時候可以訪問、刪除的元素都是此前最後存入的那個元素,確定了一種默認的訪問順序。

  由於棧數據結構只允許在一端進行操作,因爲按照後進先出(LIFO,Last In First Out)的原理運作。

  

 

  

 

  棧可以用順序表實現,也可以用鏈表實現。

  1、棧的操作:

    Stack():創建一個新的空棧

    push(item):添加一個新的元素item到棧頂

    pop():彈出棧頂元素

    peek():返回棧頂元素

    is_empty():判斷棧是否爲空

    size():返回棧的元素個數

  2、代碼實現

    

 1 # coding=utf-8
 2 
 3 
 4 class Stack:
 5     """"""
 6     def __init__(self):
 7         self.items = []
 8 
 9     def is_empty(self):
10         """判斷是否爲空"""
11         return self.items == []
12 
13     def push(self, item):
14         """加入元素"""
15         self.items.append(item)
16 
17     def pop(self):
18         """彈出元素"""
19         return self.items.pop()
20 
21     def peek(self):
22         """返回棧頂元素"""
23         return self.items[len(self.items) - 1]
24 
25     def size(self):
26         """返回棧的元素個數"""
27         return len(self.items)
28 
29 
30 if __name__ == "__main__":
31     stack = Stack()
32     stack.push("hello")
33     stack.push("world")
34     stack.push("stack")
35     print(stack.size())     # 3
36     print(stack.peek())     # stack
37     print(stack.pop())      # stack
38     print(stack.pop())      # world
39     print(stack.pop())      # hello

 

四、隊列

  隊列(queue)是隻允許在一端進行插入操作,而在另一端進行刪除操作的線性表。

  隊列是一種先進先出(FIFO,First In First Out)的線性表。允許插入的一端爲隊尾,允許刪除的一端爲隊頭。隊列不允許在中間部位進行操作!假設隊列 q=(a1, a2, ,..., an),那麼a1就是隊頭元素,而an是隊尾元素。這樣在刪除時,總是從a1開始,而插入時,總是在隊列最後。

  

 

  1、隊列的實現(同棧一樣,隊列也可以用順序表或者鏈表實現):

    隊列的操作:

 

      Queue():創建一個空的隊列

 

      enqueue(item):往隊列中添加一個item元素

 

      dequeue():從隊列頭部刪除一個元素

 

      is_empty():判斷一個隊列是否爲空

 

      size():返回隊列的大小

      

 

 1 # coding=utf-8
 2 
 3 
 4 class Queue:
 5     """隊列"""
 6     def __init__(self):
 7         self.items = []
 8 
 9     def is_empty(self):
10         return self.items == []
11 
12     def enqueue(self, item):
13         """添加元素"""
14         self.items.insert(0, item)
15 
16     def dequeue(self):
17         """從隊列頭部刪除一個元素"""
18         return self.items.pop()
19 
20     def size(self):
21         return len(self.items)
22 
23 
24 if __name__ == "__main__":
25     q = Queue()
26     q.enqueue("hello")
27     q.enqueue("world")
28     q.enqueue("queue")
29     print(q.size())
30     print(q.dequeue())      # hello
31     print(q.dequeue())      # world
32     print(q.dequeue())      # queue

 

  2、雙端隊列的實現

    雙端隊列(deque,全名 double-ended queue),是一種具有隊列和棧的性質的數據結構。

    雙端隊列中的元素可以從兩端彈出,其限定插入和刪除操作在表的兩端進行。雙端隊列可以在隊列任意一端入隊和出隊。

    

    

    操作:

      Deque():創建一個空的雙端隊列

      add_front(item):從隊頭加入一個item元素

      add_rear(item):從隊尾加入一個item元素

      remove_front():從隊頭刪除一個元素

      remove_rear():從隊尾刪除一個元素

      is_empty():判斷雙端隊列是否爲空

      size():返回隊列的大小

 1 # coding=utf-8
 2 
 3 
 4 class Deque:
 5     """雙端隊列"""
 6     def __init__(self):
 7         self.items = []
 8 
 9     def add_front(self, item):
10         """從隊頭加入一個元素"""
11         self.items.insert(0, item)
12 
13     def add_rear(self, item):
14         """從隊尾加入一個元素"""
15         self.items.append(item)
16 
17     def remove_front(self):
18         """從隊頭刪除一個元素"""
19         return self.items.pop(0)
20 
21     def remove_rear(self):
22         """從隊尾刪除一個元素"""
23         return self.items.pop()
24 
25     def is_empty(self):
26         """是否爲空"""
27         return self.items == []
28 
29     def size(self):
30         """隊列長度"""
31         return len(self.items)
32 
33 
34 if __name__ == "__main__":
35     deque = Deque()
36     deque.add_front(1)
37     deque.add_front(2)
38     deque.add_rear(3)
39     deque.add_rear(4)
40     print(deque.size())             # 4
41     print(deque.remove_front())     # 2
42     print(deque.remove_front())     # 1
43     print(deque.remove_rear())      # 4
44     print(deque.remove_rear())      # 3

 

 

 

  

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章