🧱用Python 與 串列 來實現 Stack(堆疊)

更新於 發佈於 閱讀時間約 1 分鐘

在之前的教學中,已經學會了Node和Linked List的實作,
用Python實現了單向鏈結串列Singly linked list雙向鏈結串列Doubly linked list

今天要承接之前打下的基礎,用雙向鏈結串列來實作Stack 堆疊。


定義

是一個線性長條狀的資料結構。

規定推入資料取出資料都必須在頂端TOP操作。

(就像餐廳疊盤子,或者,書桌上疊書本的那種感覺。)

也由於這項規定,Stack先天具有先進後出First-in Last-out的這種特質。

raw-image

支援的操作與介面


stack.push(data)

推入一筆新資料data進入stack的頂端。

raw-image

stack.top()

取得stack頂端目前的資料。

raw-image

stack.pop()

取得stack頂端目前的資料,並且將此資料從stack頂端移除。

raw-image

len( stack )

取得stack的大小,也就是現在有幾筆資料存在stack裡。

raw-image

stack.is_empty()

判斷stack是否為空。

raw-image

優點

1.在頂端新增、取得、移除資料是O(1) 常數時間

2.可以根據需求動態延伸或縮短,有效利用記憶體空間。


缺點

1.不支援random acess,不支援Stack[ index ] 這種操作。

2.如果要取得下層的資料,必須先將頂端的資料移除


Stack的class定義 與 建構子(初始化函式)

class Stack:

  def __init__(self):
# 底層用 雙向鏈結串列來實作 Stack
    self.stack = LinkedList()

Stack常見的function實現


1.在stack頂端插入新元素


直接在stack頂端放入新元素,對應的底層實作就是在尾巴串上新結點。


時間複雜度: O(1) 常數時間

  def push(self, data):

    self.stack.insertAtTail(data)
    return

2.讀取stack頂端元素


直接返回stack頂端的元素,對應的底層實作就是返回尾巴結點的資料。

實作上有個細節需要留意,假如stack是空的,直接返回None即可。


時間複雜度: O(1) 常數時間

  def top(self):

    if self.stack.tail is None:
      return None

    return self.stack.tail.data

3.從stack頂端取出舊元素


直接返回並且移除stack頂端的元素,
對應的底層實作就是在返回並且移除尾巴結點的資料。


時間複雜度: O(1) 常數時間

  def pop(self):
    return self.stack.removeAtTail()

4.取得stack的長度


返回stack的長度,也代表stack內部資料的總數量。

對應的底層實作就是在返回串列的長度。


時間複雜度: O(1) 常數時間

  def __len__(self):
    return len(self.stack)

5.檢查stack是否為空


如果stack 的長度為0,代表stack是空的,內部沒有任何資料。


時間複雜度: O(1) 常數時間

  def is_empty(self):
    return 0 == self.stack.size

完整的Stack實作和程式碼,
底層是Doubly Linked List雙向鏈結串列。


class Node:
def __init__(self, data):
# 儲存節點的資料
self.data = data

# 指向下一個節點,初始化為空 None
self.next = None

# 指向前一個節點,初始化為空 None
self.prev = None


class LinkedList:
def __init__(self):

# 頭部節點初始化為空
self.head = None
# 尾巴節點初始化為空
self.tail = None

# 串列長度初始化為0
self.size = 0


def insertAtHead(self,data):
new_node = Node(data)

self.size += 1

# If the list is empty
if self.tail is None:
self.head = new_node
self.tail = new_node
else:

# Update linkage
new_node.next = self.head
self.head.prev = new_node

self.head = new_node
return


def insertAtTail(self, data):
new_node = Node(data)

self.size += 1

# If the list is empty
if self.head is None:
self.head = new_node
self.tail = new_node
else:

# Update linkage
self.tail.next = new_node
new_node.prev = self.tail

self.tail = new_node

return


def removeAtHead(self):
if self.head is None:
return
else:
self.size -= 1
oldHead = self.head

# Update linkage
if self.head == self.tail:
self.tail = None
self.head = None
else:
self.head = self.head.next

# Break linkage
oldHead.next = None

if self.head:
self.head.prev = None

return oldHead


def removeAtTail(self):
if self.tail is None:
return
else:
self.size -= 1
oldTail = self.tail

# Update linkage
if self.tail == self.head:
self.tail = None
self.head = None
else:
self.tail = self.tail.prev

# Break linkage
oldTail.prev = None

if self.tail:
self.tail.next = None

return oldTail


def traverse(self):

print("Forward traversal")
cur = self.head

while cur:
print(cur.data, end=' <-> ')
cur = cur.next
print("None")

print(f"Size of singly linked list: {self.size} \n", )
return


def backward_traverse(self):

print("Backward traversal")
cur = self.tail

while cur:
print(cur.data, end=' <-> ')
cur = cur.prev
print("None")

print(f"Size of singly linked list: {self.size} \n", )
return


def search(self, target):
cur = self.head
while cur:
if cur.data == target:
return cur

cur = cur.next

return None


def removeAfter(self, node):

self.size -= 1

removed = node.next
the_one_after_removal = node.next.next if node.next else None

# Update linkage
if the_one_after_removal:
the_one_after_removal.prev = node

node.next = the_one_after_removal
# Break linkage
if removed:
removed.next = None
removed.prev = None

return removed


def insertAfter(self, node, new_node):

self.size += 1

the_one_after_insertion = node.next

# Update linkage
node.next = new_node
new_node.prev = node

the_one_after_insertion.prev = new_node
new_node.next = the_one_after_insertion
return


def __contains__(self, item):

cur = self.head
while cur:
if cur.data == item:
return True

cur = cur.next

return False


def __len__(self):
return self.size


class Stack:
def __init__(self):
self.stack = LinkedList()

def push(self, data):
self.stack.insertAtTail(data)
return

def pop(self):
return self.stack.removeAtTail()

def top(self):
if self.stack.tail is None:
return None

return self.stack.tail.data

def is_empty(self):
return 0 == self.stack.size

def __len__(self):
return len(self.stack)

def __contains__(self, item):
return item in self.stack

def traverse(self):
self.stack.traverse()
return


def test():
print(f"Empty stack\n")
stack = Stack()

for i in range(1, 5):
print(f"Push {i}...")
stack.push(i)

print()
print(f"Is stack empty? {stack.is_empty()}")
print(f"Size of stack: { len(stack) } ")
print()

for i in range(1,6):
print(f"Is {i} in stack? = { i in stack}")

print()
stack.traverse()

print(f"Top of stack = {stack.top()}")
stack.pop()

print(f"Top of stack = {stack.top()}")
stack.pop()

print(f"Top of stack = {stack.top()}")
stack.pop()

print(f"Top of stack = {stack.top()}")
stack.pop()

print(f"Is stack empty? {stack.is_empty()}")

return

if __name__ == "__main__":
test()

測試輸出

Empty stack

Push 1...
Push 2...
Push 3...
Push 4...

Is stack empty? False
Size of stack: 4

Is 1 in stack? = True
Is 2 in stack? = True
Is 3 in stack? = True
Is 4 in stack? = True
Is 5 in stack? = False

Forward traversal
1 <-> 2 <-> 3 <-> 4 <-> None
Size of singly linked list: 4

Top of stack = 4
Top of stack = 3
Top of stack = 2
Top of stack = 1
Is stack empty? True

結語


這篇我們透過Linked List作為底層的輔助資料結構,來實現完整的Stack class 和 interface function。

讀者可以搭配先前的教學文章,一起復習相關的資料結構。


Stack相關的演算法練習題與詳解


Stack堆疊應用題 合法括號配對字串 Leetcode #20_Valid Parentheses


一魚多吃 用stack來解 最長合法括弧字串 Leetcode #32 Longest Valid Parenthese


應用題 用Stack實作Queue_Leetcode #232 Implement Queue using Stacks


一魚多吃 用stack來解 Backspace String Compare_Leetcode #844


一魚多吃 用stack來驗證 合法括弧字串 678. Valid Parenthesis String


用stack 模擬陣列生成 Build Array w/ Stack Operations_Leetcode 1441

留言
avatar-img
留言分享你的想法!
話說太早了🤣🤣🤣我對你絕對是真愛~進來留個言好困難啊!
小松鼠-avatar-img
發文者
2024/08/22
林燃(創作小說家) 與爾同消萬古愁
現在好一點了,沒這麽卡🙄
小松鼠-avatar-img
發文者
2024/08/22
林燃(創作小說家)
小松鼠-avatar-img
發文者
2024/08/27
動手學Python 的引言與目錄提及了這篇文章,趕快過去看看吧!
avatar-img
小松鼠的演算法樂園
95會員
427內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
2024/10/10
從Python 內建deque資料結構的角度切入, 同時了解deque 與 FIFO Queue相關的function用法。 collections.deque是一種兩端點皆可進出的雙端佇列 在兩端點高效地在O(1)常數時間內添加和刪除元素。 這使得deque非常適合實現FIFO Queue
Thumbnail
2024/10/10
從Python 內建deque資料結構的角度切入, 同時了解deque 與 FIFO Queue相關的function用法。 collections.deque是一種兩端點皆可進出的雙端佇列 在兩端點高效地在O(1)常數時間內添加和刪除元素。 這使得deque非常適合實現FIFO Queue
Thumbnail
2024/09/27
井字遊戲(OOXX)的遊戲描述 Tic Tac Toe(井字遊戲)是經典的雙人棋盤遊戲,在一個3x3的方格中進行。 每回合兩個玩家輪流選一個位置,先讓自己的符號(是 X 或 O)在 水平線、垂直線或對角線上連成一線的玩家宣告獲勝。
Thumbnail
2024/09/27
井字遊戲(OOXX)的遊戲描述 Tic Tac Toe(井字遊戲)是經典的雙人棋盤遊戲,在一個3x3的方格中進行。 每回合兩個玩家輪流選一個位置,先讓自己的符號(是 X 或 O)在 水平線、垂直線或對角線上連成一線的玩家宣告獲勝。
Thumbnail
2024/09/23
深入探討圖(Graph)的基本概念 及 最短路徑Shortest Path的尋找。 我們專注於廣度優先搜尋(BFS)演算法,以等權圖的最短路徑為例, 詳細說明如何利用BFS從起點擴散到終點,並且提供詳細的程式碼範例。 透過實作,讀者能夠更清楚理解圖論及BFS的應用,並體會水波紋擴散模型的重要性。
Thumbnail
2024/09/23
深入探討圖(Graph)的基本概念 及 最短路徑Shortest Path的尋找。 我們專注於廣度優先搜尋(BFS)演算法,以等權圖的最短路徑為例, 詳細說明如何利用BFS從起點擴散到終點,並且提供詳細的程式碼範例。 透過實作,讀者能夠更清楚理解圖論及BFS的應用,並體會水波紋擴散模型的重要性。
Thumbnail
看更多
你可能也想看
Thumbnail
沙龍一直是創作與交流的重要空間,這次 vocus 全面改版了沙龍介面,就是為了讓好內容被好好看見! 你可以自由編排你的沙龍首頁版位,新版手機介面也讓每位訪客都能更快找到感興趣的內容、成為你的支持者。 改版完成後可以在社群媒體分享新版面,並標記 @vocus.official⁠ ♥️ ⁠
Thumbnail
沙龍一直是創作與交流的重要空間,這次 vocus 全面改版了沙龍介面,就是為了讓好內容被好好看見! 你可以自由編排你的沙龍首頁版位,新版手機介面也讓每位訪客都能更快找到感興趣的內容、成為你的支持者。 改版完成後可以在社群媒體分享新版面,並標記 @vocus.official⁠ ♥️ ⁠
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
Array可以說是各種語言除了基本型別之外,最常用的資料型別與容器之一了。 Array 這種連續格子狀的資料結構,在Python要怎麼表達呢? 建立一個空的陣列 最簡單也最直接的寫法就是 array = [] # Python list [] 就對應到大家熟知的array 陣列型態的資料結
Thumbnail
Array可以說是各種語言除了基本型別之外,最常用的資料型別與容器之一了。 Array 這種連續格子狀的資料結構,在Python要怎麼表達呢? 建立一個空的陣列 最簡單也最直接的寫法就是 array = [] # Python list [] 就對應到大家熟知的array 陣列型態的資料結
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
題目會給我們個陣列target,問我們從整數串流中1~n之中,如何透過stack 的 push和pop來模擬生成target指定的形式?
Thumbnail
題目會給我們個陣列target,問我們從整數串流中1~n之中,如何透過stack 的 push和pop來模擬生成target指定的形式?
Thumbnail
題目會給我們一組定義好的Stack 堆疊的介面,要求底層用兩個或一個Queue來實現。 也就是說,要求我們用一個或兩個FIFO的Queues去實作出一個LIFO的Stack
Thumbnail
題目會給我們一組定義好的Stack 堆疊的介面,要求底層用兩個或一個Queue來實現。 也就是說,要求我們用一個或兩個FIFO的Queues去實作出一個LIFO的Stack
Thumbnail
Array 在記憶體中連續分配,而且元素類型是一樣的,長度不變 優點:讀取快,可以使用座標訪問 缺點:新增、刪除慢 記憶體: 📷 範例程式碼: ArrayList 不定長度,在記憶體中連續分配的,元素沒有類型限制,任何元素都是當成object處理,如果是值類型,會有裝箱的操作 優點:讀取快 缺點:
Thumbnail
Array 在記憶體中連續分配,而且元素類型是一樣的,長度不變 優點:讀取快,可以使用座標訪問 缺點:新增、刪除慢 記憶體: 📷 範例程式碼: ArrayList 不定長度,在記憶體中連續分配的,元素沒有類型限制,任何元素都是當成object處理,如果是值類型,會有裝箱的操作 優點:讀取快 缺點:
Thumbnail
本文會介紹靜態結構 - 串列(List)與動態結構 - 鏈結串列(Linked List)來實踐陣列的不同功能,如:刪除、計算元素個數與反轉。
Thumbnail
本文會介紹靜態結構 - 串列(List)與動態結構 - 鏈結串列(Linked List)來實踐陣列的不同功能,如:刪除、計算元素個數與反轉。
Thumbnail
本文延續從上一篇文章的內容,繼續來介紹如何分別以靜態結構 - 串列(List)及鏈結串列(Linked List)兩種不同的方式來建立實作陣列的新增及修改元素功能。
Thumbnail
本文延續從上一篇文章的內容,繼續來介紹如何分別以靜態結構 - 串列(List)及鏈結串列(Linked List)兩種不同的方式來建立實作陣列的新增及修改元素功能。
Thumbnail
陣列是Python語言的最基礎也最容易實作的資料結構,主要可以透過兩種方式在Python上實踐陣列,其中一種是靜態結構 - 串列(List),另一種則是動態結構 - 鏈結串列(Linked List)。 我們會依序介紹這兩種作法如何在Python上執行陣列的相關功能,並比較兩種方法之間的差異。
Thumbnail
陣列是Python語言的最基礎也最容易實作的資料結構,主要可以透過兩種方式在Python上實踐陣列,其中一種是靜態結構 - 串列(List),另一種則是動態結構 - 鏈結串列(Linked List)。 我們會依序介紹這兩種作法如何在Python上執行陣列的相關功能,並比較兩種方法之間的差異。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News