python數據結構與算法-05_棧

棧這個詞實際上在計算機科學里使用很多,除了數據結構外,還有內存里的棧區 (和堆對應),熟悉 C 系語言的話應該不會陌生。
上一章我們講到了先進先出 queue,其實用 python 的內置類型 collections.deque 或者我們自己實現的 LinkedList 來實現它都很簡單。
本章我們講講 后進先出的棧。

生活中的數據結構:

  • 棧。好比在桶里頭放盤子,先放的盤子放在了底下,后來的盤子放在上邊。你要拿的時候,也是先拿最上邊的。

棧其實也很簡單,因為基礎操作就倆,一個 push 和一個 pop,咦,咋和隊列一樣的?
確實方法名字一樣,但是得到的結果可是不同的。

棧 ADT

上一章我介紹了我們怎樣選取恰到的數據結構來實現新的 ADT?你能想到這里我們應該使用之前提到的哪個數據結構來實現嗎?
你的大腦可能開始高(gui)速(su)旋轉了,上幾章學過的 array, list, deque, LinkedList, CircularDoubleLinkedList, queue
等在大腦里呼嘯而過,這個時候可能已經一臉愁容了,到底該選啥?

還用問嘛,當然是時間復雜度最小的啦,大部分情況下空間都是夠用的。
其實你會發現棧比隊列還簡單,因為它只在頂上操作(想象裝著盤子的桶),如果有一種數據結構能方便在尾部增減元素不就滿足需求了嗎。
這個時候如果你忘記了,可以翻翻前幾章,看看哪個數據結構符合要求。

想一下,似乎 CircularDoubleLinkedList 循環雙端隊列是滿足的,因為增刪最后一個元素都是 O(1)。
不過看了下示例代碼,似乎沒有 pop() 方法,對,因為我已經把實現 deque 作為思考題了。😂
如果之前你沒寫出來也沒關系,這里我們會再實現它。

視頻里我們將借助 CircularDoubleLinkedList 實現 雙端隊列 Deque ,并且用 Deque 實現 Stack。

Stack over flow 什么鬼?

嗯,stackoverflow 不是一個程序員問答網站嗎?沒錯。
函數的臨時變量是存儲在棧區的,如果你不幸寫了一個沒有出口的遞歸函數,就會這個錯。不信你試試:

def infinite_fib(n):return infinite_fib(n-1) + infinite_fib(n-2)
infinite_fib(10)

一大段輸出之后就會出現異常: RecursionError: maximum recursion depth exceeded。
后邊會講到遞歸,遞歸是初學者比較難理解的概念,在樹的遍歷等地方還會看到它。

源碼

# -*- coding: utf-8 -*-# NOTE: 這里拷貝的 double_link_list.py 里的代碼from collections import dequeclass Node(object):def __init__(self, value=None, prev=None, next=None):self.value, self.prev, self.next = value, prev, nextclass CircularDoubleLinkedList(object):"""循環雙端鏈表 ADT多了個循環其實就是把 root 的 prev 指向 tail 節點,串起來"""def __init__(self, maxsize=None):self.maxsize = maxsizenode = Node()node.next, node.prev = node, nodeself.root = nodeself.length = 0def __len__(self):return self.lengthdef headnode(self):return self.root.nextdef tailnode(self):return self.root.prevdef append(self, value):    # O(1), 你發現一般不用 for 循環的就是 O(1),有限個步驟if self.maxsize is not None and len(self) >= self.maxsize:raise Exception('LinkedList is Full')node = Node(value=value)tailnode = self.tailnode() or self.roottailnode.next = nodenode.prev = tailnodenode.next = self.rootself.root.prev = nodeself.length += 1def appendleft(self, value):if self.maxsize is not None and len(self) >= self.maxsize:raise Exception('LinkedList is Full')node = Node(value=value)if self.root.next is self.root:   # emptynode.next = self.rootnode.prev = self.rootself.root.next = nodeself.root.prev = nodeelse:node.prev = self.rootheadnode = self.root.nextnode.next = headnodeheadnode.prev = nodeself.root.next = nodeself.length += 1def remove(self, node):      # O(1),傳入node 而不是 value 我們就能實現 O(1) 刪除"""remove:param node  # 在 lru_cache 里實際上根據key 保存了整個node:"""if node is self.root:returnelse:    #node.prev.next = node.nextnode.next.prev = node.prevself.length -= 1return nodedef iter_node(self):if self.root.next is self.root:returncurnode = self.root.nextwhile curnode.next is not self.root:yield curnodecurnode = curnode.nextyield curnodedef __iter__(self):for node in self.iter_node():yield node.valuedef iter_node_reverse(self):"""相比單鏈表獨有的反序遍歷"""if self.root.prev is self.root:returncurnode = self.root.prevwhile curnode.prev is not self.root:yield curnodecurnode = curnode.prevyield curnode############################################################
# 分割線,下邊是本章 內容實現
############################################################class Deque(CircularDoubleLinkedList):   # 注意這里我們用到了繼承,嗯,貌似我說過不會用啥 OOP 特性的,抱歉def pop(self):"""刪除尾節點"""if len(self) == 0:raise Exception('empty')tailnode = self.tailnode()value = tailnode.valueself.remove(tailnode)return valuedef popleft(self):if len(self) == 0:raise Exception('empty')headnode = self.headnode()value = headnode.valueself.remove(headnode)return valuedef test_deque():dq = Deque()dq.append(1)dq.append(2)assert list(dq) == [1, 2]dq.appendleft(0)assert list(dq) == [0, 1, 2]dq.pop()assert list(dq) == [0, 1]dq.popleft()assert list(dq) == [1]dq.pop()assert len(dq) == 0class Stack(object):def __init__(self):self.deque = Deque()   # 你可以很容易替換為 python 內置的 collections.dequedef push(self, value):self.deque.append(value)def pop(self):return self.deque.pop()class Stack2(object):def __init__(self):self._deque = deque()def push(self, value):return self._deque.append(value)def pop(self):return self._deque.pop()def empty(self):return len(self._deque) == 0def test_stack():s = Stack()s.push(0)s.push(1)s.push(2)assert s.pop() == 2assert s.pop() == 1assert s.pop() == 0import pytest    # pip install pytestwith pytest.raises(Exception) as excinfo:   # 我們來測試是否真的拋出了異常s.pop()assert 'empty' in str(excinfo.value)if __name__ == '__main__':test_stack()

數據結構頭腦風暴法

當我們不知道使用什么數據結構來解決問題的時候,《程序員面試金典》這本書的第六章提到了一種方式叫做『數據結構頭腦風暴法』。
這種笨方法就是快速過一遍數據結構的列表,然后逐一嘗試各種數據結構看看哪個最適合。

在你實現一個更高級的數據結構的時候,如果腦子沒有思路,不妨嘗試下這個方法,迅速過一遍你所知道的數據結構,看看哪種最適合。(從每個操作的時間復雜度和空間復雜度分析尋找最優解)

思考題

  • 上一章我們用數組實現了隊列,其實也能用數組來實現棧,你能自己用數組來實現一個棧的 ADT 嗎?
  • 實際上借助 python 內置的 list/collections.deque 結構就很容易實現一個棧,請你嘗試實現,本章我們全部使用自己編寫的數據結構而沒用到 python 內置的數據結構。
  • 這里我們自己實現了 Deque,你能用 python 內置的 collections.deque 實現棧嗎?有輪子能直接用的話看起來就簡單多了,這里我們為了學習數據結構的實現就避免了直接使用內置結構
  • 哪些經典算法里使用到了棧呢?

Leetcode 練習

https://leetcode.com/problems/implement-queue-using-stacks/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/161548.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/161548.shtml
英文地址,請注明出處:http://en.pswp.cn/news/161548.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【C語法學習】26 - strcmp()函數

文章目錄 1 函數原型2 參數3 返回值4 比較機制5 示例5.1 示例1 1 函數原型 strcmp():比較str1指向的字符串和str2指向的字符串,函數原型如下: int strcmp(const char *str1, const char *str2);2 參數 strcmp()函數有兩個參數str1和str2&a…

HCIP-四、MUX-vlanSuper-vlan+端口安全

四、MUX-vlan&Super-vlan端口安全 MUX-vlan實驗拓撲實驗需求及解法1. 在SW1/2/3分別創建vlan10 20 30 402. SW1/2/3之間使用trunk鏈路,僅允許vlan10 20 30 40 通過。3. SW與PC/Server之間使用access鏈路。4. ping驗證: Super-vlan端口安全實驗拓撲實…

【騰訊云云上實驗室-向量數據庫】騰訊云開創新時代,發布全新向量數據庫Tencent Cloud VectorDB

前言 隨著人工智能、數據挖掘等技術的飛速發展,海量數據的存儲和分析越來越成為重要的研究方向。在海量數據中找到具有相似性或相關性的數據對于實現精準推薦、搜索等應用至關重要。傳統關系型數據庫存在一些缺陷,例如存儲效率低、查詢耗時長等問題&…

CentOS使用docker安裝OpenGauss數據庫

1.搜索OpenGauss docker search opengauss 2.選擇其中一個源拉取 docker pull docker.io/enmotech/opengauss 3.運行OpenGauss docker run --name opengauss --privilegedtrue --restartalways -d -e GS_USERNAMEpostgres -e GS_PASSWORDmyGauss2023 -p 5432:5432 docker.…

黑馬React18: ReactRouter

黑馬React: ReactRouter Date: November 21, 2023 Sum: React路由基礎、路由導航、導航傳參、嵌套路由配置 路由快速上手 1. 什么是前端路由 一個路徑 path 對應一個組件 component 當我們在瀏覽器中訪問一個 path 的時候,path 對應的組件會在頁面中進行渲染 2. …

2023年中國高壓驅動芯片分類、市場規模及發展趨勢分析[圖]

高壓驅動芯片是一種能在高壓環境下工作的集成電路,主要用于控制和驅動各種功率器件,如繼電器、電磁閥、電機、變頻器等。高壓驅動芯片根據其輸出電流的大小和形式可分為兩類恒流型和開關型。 高壓驅動芯片分類 資料來源:共研產業咨詢&#x…

藍橋杯算法雙周賽心得——迷宮逃脫(記憶化搜索)

大家好,我是晴天學長,非常經典實用的記憶化搜索題,當然也可以用dp做,我也會發dp的題解,需要的小伙伴可以關注支持一下哦!后續會繼續更新的。💪💪💪 1) .迷宮逃脫 迷官逃脫…

ubuntu操作系統中docker下Hadoop分布式前置環境配置實驗

版本: centos7 hadoop 3.1.3 java JDK:1.8 集群規劃: masterslave1slave2HDFS NameNode DataNode DataNode SecondryNameNode DataNode YARNNodeManager ResourceManage NodeManager NodeManager 1.docker容器: 把普通用戶加入到docker組&am…

opencv-Canny 邊緣檢測

Canny邊緣檢測是一種經典的圖像邊緣檢測算法,它在圖像中找到強度梯度的變化,從而識別出圖像中的邊緣。Canny邊緣檢測的優點包括高靈敏度和低誤檢率。 在OpenCV中,cv2.Canny() 函數用于執行Canny邊緣檢測。 基本語法如下: edges…

代碼隨想錄 134. 加油站

題目 在一條環路上有 n 個加油站,其中第 i 個加油站有汽油 gas[i] 升。 你有一輛油箱容量無限的的汽車,從第 i 個加油站開往第 i1 個加油站需要消耗汽油 cost[i] 升。你從其中的一個加油站出發,開始時油箱為空。 給定兩個整數數組 gas 和 cos…

本地訓練,開箱可用,Bert-VITS2 V2.0.2版本本地基于現有數據集訓練(原神刻晴)

按照固有思維方式,深度學習的訓練環節應該在云端,畢竟本地硬件條件有限。但事實上,在語音識別和自然語言處理層面,即使相對較少的數據量也可以訓練出高性能的模型,對于預算有限的同學們來說,也沒必要花冤枉…

阿里云 ACK 新升級,打造智算時代的現代化應用平臺

云布道師 今天,能想到的或是想不到的領域,對容器和 Kubernetes 的需求都居高不減,使這項技術正在真正走向無處不在。 在 2023 云棲大會上,阿里云云原生產品線容器服務負責人易立關于容器服務 ACK 在本屆亞運會上應用的介紹&#…

[crash] cxa_pure_virtual 崩潰分析與原理

摘要:工作過程中處理線上的崩潰時發現了一例cxa_pure_virtual相關的crash,直接看堆棧基本山很容易確認是有異步調用導致出發了ABI的異常。但是對于為什么會觸發cxa_pure_virtual雖然有大致的猜測但是沒有直接的證據,因此本文主要描述觸發該類…

C/C++未定義行為的例子匯總

一、什么是未定義行為? 未定義行為(Undefined Behavior)是指C語言標準未做規定的行為。同時,標準也從沒要求編譯器判斷未定義行為,所以這些行為有編譯器自行處理,在不同的編譯器可能會產生不同的結果&#…

ElasticSearch之cat aliases API

執行aliases命令,如下: curl -X GET "https://localhost:9200/_cat/aliases?pretty&vtrue" --cacert $ES_HOME/config/certs/http_ca.crt -u "elastic:ohCxPHQBEs5*lo7F9"執行結果輸出如下: alias index …

在 VSCode 中使用 GDB 進行 C/C++ 程序調試(圖文版)

(??? ),Hello我是祐言QAQ我的博客主頁:C/C語言,數據結構,Linux基礎,ARM開發板,網絡編程等領域UP🌍快上🚘,一起學習,讓我們成為一個強大的攻城獅&#xff0…

webpack loader

1、分類 2、執行順序 配置類型 執行順序是 loader1>loader2>loader3 3、使用方式 自己的第一個loader 同步loader /*** loader 就是一個函數* 當webpack 解釋資源時, 會調用相應的loader去處理* loader 接收到文件內容作為參數,返回文件內容* p…

Nginx 開源版安裝

下載 tar.gz安裝包,上傳。 解壓 [rootlocalhost ~]# tar zxvf nginx-1.21.6.tar.gz nginx-1.21.6/ nginx-1.21.6/auto/ nginx-1.21.6/conf/ nginx-1.21.6/contrib/ nginx-1.21.6/src/ ... ...安裝gcc [rootlocalhost nginx-1.21.6]# yum install -y gcc 已加載插件…

ios qt開發要點

目前關于ios qt的開發資料比較少,這里整理了幾個比較重要的開發要點,基于MacOS14 Xcode15 Qt15.5 cmake iphone真機。 cmake報錯,報錯信息如下 CMake Error at /Users/user/Qt/5.15.5/ios/lib/cmake/Qt5Core/Qt5CoreConfig.cmake:91 (m…

C#Wpf關于日志的相關功能擴展

目錄 一、日志Sink(接收器) 二、Trace追蹤實現日志 三、日志滾動 一、日志Sink(接收器) 安裝NuGet包:Serilog Sink有很多種,這里介紹兩種: Console接收器(安裝Serilog.Sinks.Console); File接收器(安裝…