为了OFFER,我加深学习,搞懂了栈
@Author:Runsrn
@Date:2020/9/8
現(xiàn)在大四基本是重刷數(shù)據(jù)結(jié)構(gòu)和算法,因?yàn)楣P試真的太重要了。 我又重溫了爭大佬專欄的棧,又鞏固了下。而且我發(fā)現(xiàn)留言區(qū)大佬的筆記很多,下面很多都是來自大佬總結(jié)的。
文章目錄
- 一、什么是棧?
- 二、為什么需要棧?
- 三、如何實(shí)現(xiàn)棧?
- 四、棧的應(yīng)用
- 1.棧在函數(shù)調(diào)用中的應(yīng)用
- 2.棧在表達(dá)式求值中的應(yīng)用(比如:34+13*9+44-12/3)
- 3.棧在括號(hào)匹配中的應(yīng)用`(比如:{}{[()]()})` Leetcode 20 題
- 4.如何實(shí)現(xiàn)瀏覽器的前進(jìn)后退功能?
- 五、思考題
- 六、20有效的擴(kuò)號(hào)
- 7、155最小棧(方法:輔助棧)
- 8、 232用棧實(shí)現(xiàn)隊(duì)列
- 9、844比較含退格的字符串
- 10、682棒球比賽
- 11、496下一個(gè)更大元素 I
一、什么是棧?
1、棧是一種操作受限的數(shù)據(jù)結(jié)構(gòu),只支持入棧和出棧操作。
2、典型的“棧”結(jié)構(gòu):后進(jìn)者先出,先進(jìn)者后出。
3、從棧的操作特性上來看,棧是一種“操作受限”的線性表,只允許在一端插入和刪除數(shù)據(jù)。
4、特定的數(shù)據(jù)結(jié)構(gòu)是對特定場景的抽象,而且,數(shù)組或鏈表暴露了太多的操作接口,操作上的確靈活自由,但使用時(shí)就比較不可控,自然也就更容易出錯(cuò)。
二、為什么需要棧?
1.棧是一種操作受限的數(shù)據(jù)結(jié)構(gòu),其操作特性用數(shù)組和鏈表均可實(shí)現(xiàn)。
2.但,任何數(shù)據(jù)結(jié)構(gòu)都是對特定應(yīng)用場景的抽象,數(shù)組和鏈表雖然使用起來更加靈活,但卻暴露了幾乎所有的操作,難免會(huì)引發(fā)錯(cuò)誤操作的風(fēng)險(xiǎn)。
3.所以,當(dāng)某個(gè)數(shù)據(jù)集合只涉及在某端插入和刪除數(shù)據(jù),且滿足后進(jìn)者先出,先進(jìn)者后出的操作特性時(shí),我們應(yīng)該首選棧這種數(shù)據(jù)結(jié)構(gòu)。
三、如何實(shí)現(xiàn)棧?
1.棧的API ,Java代碼實(shí)現(xiàn),代碼來自爭大佬。
// 基于數(shù)組實(shí)現(xiàn)的順序棧 public class ArrayStack {private String[] items; // 數(shù)組private int count; // 棧中元素個(gè)數(shù)private int n; //棧的大小// 初始化數(shù)組,申請一個(gè)大小為n的數(shù)組空間public ArrayStack(int n) {this.items = new String[n];this.n = n;this.count = 0;}// 入棧操作public boolean push(String item) {// 數(shù)組空間不夠了,直接返回false,入棧失敗。if (count == n) return false;// 將item放到下標(biāo)為count的位置,并且count加一items[count] = item;++count;return true;}// 出棧操作public String pop() {// 棧為空,則直接返回nullif (count == 0) return null;// 返回下標(biāo)為count-1的數(shù)組元素,并且棧中元素個(gè)數(shù)count減一String tmp = items[count-1];--count;return tmp;} }2.數(shù)組實(shí)現(xiàn)(自動(dòng)擴(kuò)容)
時(shí)間復(fù)雜度分析:根據(jù)均攤復(fù)雜度的定義,可以得數(shù)組實(shí)現(xiàn)(自動(dòng)擴(kuò)容)符合大多數(shù)情況是O(1)級別復(fù)雜度,個(gè)別情況是O(n)級別復(fù)雜度,比如自動(dòng)擴(kuò)容時(shí),會(huì)進(jìn)行完整數(shù)據(jù)的拷貝。
空間復(fù)雜度分析:在入棧和出棧的過程中,只需要一兩個(gè)臨時(shí)變量存儲(chǔ)空間,所以O(shè)(1)級別。我們說空間復(fù)雜度的時(shí)候,是指除了原本的數(shù)據(jù)存儲(chǔ)空間外,算法運(yùn)行還需要額外的存儲(chǔ)空間。
3.鏈表實(shí)現(xiàn)
時(shí)間復(fù)雜度分析:壓棧和彈棧的時(shí)間復(fù)雜度均為O(1)級別,因?yàn)橹恍韪膯蝹€(gè)節(jié)點(diǎn)的索引即可。
空間復(fù)雜度分析:在入棧和出棧的過程中,只需要一兩個(gè)臨時(shí)變量存儲(chǔ)空間,所以O(shè)(1)級別。我們說空間復(fù)雜度的時(shí)候,是指除了原本的數(shù)據(jù)存儲(chǔ)空間外,算法運(yùn)行還需要額外的存儲(chǔ)空間。
四、棧的應(yīng)用
1.棧在函數(shù)調(diào)用中的應(yīng)用
操作系統(tǒng)給每個(gè)線程分配了一塊獨(dú)立的內(nèi)存空間,這塊內(nèi)存被組織成“棧”這種結(jié)構(gòu),用來存儲(chǔ)函數(shù)調(diào)用時(shí)的臨時(shí)變量。每進(jìn)入一個(gè)函數(shù),就會(huì)將其中的臨時(shí)變量作為棧幀入棧,當(dāng)被調(diào)用函數(shù)執(zhí)行完成,返回之后,將這個(gè)函數(shù)對應(yīng)的棧幀出棧。
2.棧在表達(dá)式求值中的應(yīng)用(比如:34+13*9+44-12/3)
利用兩個(gè)棧,其中一個(gè)用來保存操作數(shù),另一個(gè)用來保存運(yùn)算符。我們從左向右遍歷表達(dá)式,當(dāng)遇到數(shù)字,我們就直接壓入操作數(shù)棧;當(dāng)遇到運(yùn)算符,就與運(yùn)算符棧的棧頂元素進(jìn)行比較,若比運(yùn)算符棧頂元素優(yōu)先級高,就將當(dāng)前運(yùn)算符壓入棧,若比運(yùn)算符棧頂元素的優(yōu)先級低或者相同,從運(yùn)算符棧中取出棧頂運(yùn)算符,從操作數(shù)棧頂取出2個(gè)操作數(shù),然后進(jìn)行計(jì)算,把計(jì)算完的結(jié)果壓入操作數(shù)棧,繼續(xù)比較。
3.棧在括號(hào)匹配中的應(yīng)用(比如:{}{[()]()}) Leetcode 20 題
用棧保存為匹配的左括號(hào),從左到右一次掃描字符串,當(dāng)掃描到左括號(hào)時(shí),則將其壓入棧中;當(dāng)掃描到右括號(hào)時(shí),從棧頂取出一個(gè)左括號(hào),如果能匹配上,則繼續(xù)掃描剩下的字符串。如果掃描過程中,遇到不能配對的右括號(hào),或者棧中沒有數(shù)據(jù),則說明為非法格式。
當(dāng)所有的括號(hào)都掃描完成之后,如果棧為空,則說明字符串為合法格式;否則,說明未匹配的左括號(hào)為非法格式。
4.如何實(shí)現(xiàn)瀏覽器的前進(jìn)后退功能?
我們使用兩個(gè)棧X和Y,我們把首次瀏覽的頁面依次壓如棧X,當(dāng)點(diǎn)擊后退按鈕時(shí),再依次從棧X中出棧,并將出棧的數(shù)據(jù)一次放入Y棧。當(dāng)點(diǎn)擊前進(jìn)按鈕時(shí),我們依次從棧Y中取出數(shù)據(jù),放入棧X中。當(dāng)棧X中沒有數(shù)據(jù)時(shí),說明沒有頁面可以繼續(xù)后退瀏覽了。當(dāng)Y棧沒有數(shù)據(jù),那就說明沒有頁面可以點(diǎn)擊前進(jìn)瀏覽了。
五、思考題
我們在講棧的應(yīng)用時(shí),講到用函數(shù)調(diào)用棧來保存臨時(shí)變量,為什么函數(shù)調(diào)用要用“棧”來保存臨時(shí)變量呢?用其他數(shù)據(jù)結(jié)構(gòu)不行嗎?
答:因?yàn)楹瘮?shù)調(diào)用的執(zhí)行順序符合后進(jìn)者先出,先進(jìn)者后出的特點(diǎn)。比如函數(shù)中的局部變量的生命周期的長短是先定義的生命周期長,后定義的生命周期短;還有函數(shù)中調(diào)用函數(shù)也是這樣,先開始執(zhí)行的函數(shù)只有等到內(nèi)部調(diào)用的其他函數(shù)執(zhí)行完畢,該函數(shù)才能執(zhí)行結(jié)束。
正是由于函數(shù)調(diào)用的這些特點(diǎn),根據(jù)數(shù)據(jù)結(jié)構(gòu)是特定應(yīng)用場景的抽象的原則,我們優(yōu)先考慮棧結(jié)構(gòu)。
我們都知道,JVM 內(nèi)存管理中有個(gè)“堆棧”的概念。棧內(nèi)存用來存儲(chǔ)局部變量和方法調(diào)用,堆內(nèi)存用來存儲(chǔ) Java 中的對象。那 JVM 里面的“棧”跟我們這里說的“棧”是不是一回事呢?如果不是,那它為什么又叫作“棧”呢?
答:JVM里面的棧和我們這里說的是一回事,被稱為方法棧。和前面函數(shù)調(diào)用的作用是一致的,用來存儲(chǔ)方法中的局部變量。
leetcode上關(guān)于棧的題目大家可以先做20,155,232,844,224,682,496。我請重新再寫了下,發(fā)現(xiàn)難度沒有之前的大。
六、20有效的擴(kuò)號(hào)
''' @Author: Runsen @WeChat:RunsenLiu @微信公眾號(hào): Python之王 @CSDN: https://blog.csdn.net/weixin_44510615 @Github: https://github.com/MaoliRUNsen @Date: 2020/9/8 ''' class Solution:def isValid(self, s: str) -> bool:stack = []d = {"{": "}", "[": "]", "(": ")"}for i in s:if i in d:stack.append(i)else:# 記住如果使用了stack.pop() 那么就已經(jīng)默認(rèn)刪除了 pop這里已經(jīng)執(zhí)行了# 還有not stack和 len(stack) == 0等價(jià)if (len(stack) == 0) or (d[stack.pop()] != i):return Falsereturn len(stack) == 07、155最小棧(方法:輔助棧)
class MinStack:def __init__(self):# 使用輔助棧,q2來存儲(chǔ)最小值self.q1 = []self.q2 = []def push(self, x):self.q1.append(x)# 這里需要判斷q2是不是為空,或者q2[-1]大于x,說明最小值不是q2[-1],if (len(self.q2) == 0) or (x <=self.q2[-1]):self.q2.append(x)else:# 那么q2就添加q2[-1】self.q2.append(self.q2[-1])def pop(self):# 刪除就是把q1和q2都刪除了self.q1.pop()self.q2.pop()def top(self):return self.q1[-1]def getMin(self):return self.q2[-1]8、 232用棧實(shí)現(xiàn)隊(duì)列
class MyQueue:def __init__(self):self.s = []def push(self, x: int) -> None:self.s.append(x)def pop(self) -> int: return self.s.pop(0)def peek(self) -> int:return self.s[0]def empty(self) -> bool:return not bool(self.s)# return not self.s 和上面同價(jià)這里,我想起了上面的有效的擴(kuò)號(hào)中的not stack,和bool stack做一個(gè)對比
>>> a = [] >>> not a True >>> bool(a) False >>> a = [1] >>> not a False >>> bool(a) Truenot a就是len(a) == 0,bool就是len(a) != 0 。感覺來的
9、844比較含退格的字符串
class Solution:def backspaceCompare(self, S: str, T: str) -> bool:# 定義一個(gè)函數(shù)返回最終的結(jié)果def solve(s):# 定義一個(gè)棧,如果沒有遇到# 就存儲(chǔ)起來stack = []for i in s:if i == "#":if len(stack) !=0:stack.pop()else:continueelse:stack.append(i)return ''.join(stack)return solve(S) == solve(T)10、682棒球比賽
class Solution:def calPoints(self, ops: List[str]) -> int:# 做法 用一個(gè)棧來記錄每輪數(shù)據(jù)stack1 = []for i in ops:if i == "C":# 直接刪除最后一個(gè)stack1.pop()elif i =="D":stack1.append(stack1[-1] * 2)elif i == "+":stack1.append(stack1[-1] + stack1[-2])else:stack1.append(int(i))return sum(stack1)11、496下一個(gè)更大元素 I
輸入: nums1 = [4,1,2], nums2 = [1,3,4,2].
輸出: [-1,3,-1]
解釋:
對于num1中的數(shù)字4,你無法在第二個(gè)數(shù)組中找到下一個(gè)更大的數(shù)字,因此輸出 -1。
對于num1中的數(shù)字1,第二個(gè)數(shù)組中數(shù)字1右邊的下一個(gè)較大數(shù)字是 3。
對于num1中的數(shù)字2,第二個(gè)數(shù)組中沒有下一個(gè)更大的數(shù)字,因此輸出 -1。
上面全部通過,以后遇到棧,就是在恐怖的算法面試中遇見了愛情!!!!!
如果你想跟博主建立親密關(guān)系,可以關(guān)注博主,或者關(guān)注博主公眾號(hào)“Python之王”,了解一個(gè)非本科程序員是如何成長的。
博主ID:潤森(weixin_44510615),希望大家點(diǎn)贊、評論、收藏
總結(jié)
以上是生活随笔為你收集整理的为了OFFER,我加深学习,搞懂了栈的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 六十七、二分查找算法及其四个变形问题
- 下一篇: 为了OFFER,我加深学习队列,现在还一