日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

数据结构杂谈(五)——栈

發(fā)布時(shí)間:2023/12/9 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构杂谈(五)——栈 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文的所有代碼均由C++編寫(xiě)

引用及參考資料:

  • 王道數(shù)據(jù)結(jié)構(gòu)
  • 大話(huà)數(shù)據(jù)結(jié)構(gòu)
  • 超硬核十萬(wàn)字!全網(wǎng)最全 數(shù)據(jù)結(jié)構(gòu) 代碼,隨便秒殺老師/面試官,我說(shuō)的_hebtu666-CSDN博客

5 棧

5.1 引入

在前面學(xué)習(xí)線(xiàn)性表的時(shí)候,我們給出了線(xiàn)性表的定義:

線(xiàn)性表是具有相同數(shù)據(jù)類(lèi)型的n個(gè)數(shù)據(jù)元素的有限序列。其中n為表長(zhǎng),當(dāng)n=0的時(shí)候線(xiàn)性表是一個(gè)空表。若用L命名線(xiàn)性表,即:L=(a1,a2,a3….,an)L = (a_1,a_2,a_3….,a_n)L=(a1?,a2?,a3?.,an?)。

實(shí)際上,棧(Stack)是一類(lèi)特殊的線(xiàn)性表,它只允許在一端進(jìn)行插入或刪除操作。棧在一些其他科目中也被叫做堆棧。這樣從數(shù)學(xué)角度來(lái)看仿佛很抽象,能不能有一個(gè)形象地比喻呢?

這里我們引入的是《大話(huà)數(shù)據(jù)結(jié)構(gòu)》中的例子。

假設(shè)現(xiàn)在有一把手槍,從結(jié)構(gòu)上看,一顆子彈打出去后,另一顆子彈就會(huì)上膛,但是如果上膛的是一顆壞掉的子彈,那手槍能夠換下一顆嗎?如果在不自己手動(dòng)拆出彈夾更換的情況下是做不到的。

這里引入的第二個(gè)例子是王道數(shù)據(jù)結(jié)構(gòu)視頻的例子。

當(dāng)在不抱起所有盤(pán)子且不移動(dòng)其他盤(pán)子的情況下,如果我們要拿起一個(gè)盤(pán)子或者放一個(gè)盤(pán)子,只能在最上面下手吧。同樣的,如果我們要吃一支烤串,在不咬爛肉的情況下想要吃到第二個(gè)肉,必須先吃第一個(gè)再吃第二個(gè)吧。

由上面的例子可以總結(jié)出,棧實(shí)際上是一個(gè)先進(jìn)后出的結(jié)構(gòu),且只能在一端進(jìn)行操作。

5.2 重要術(shù)語(yǔ)說(shuō)明

如果棧內(nèi)沒(méi)有任何一個(gè)元素,我們稱(chēng)其為空棧。

我們把允許插入和刪除的那一端叫做棧頂,另一端叫做棧底,不含任何數(shù)據(jù)元素的棧叫做空棧。由此我們可以發(fā)現(xiàn),第一個(gè)進(jìn)去的元素在最底下,所以,棧又稱(chēng)為后進(jìn)先出(Last In First Out)的線(xiàn)性表。我們也把棧的結(jié)構(gòu)叫做LIFO結(jié)構(gòu)。

棧的插入操作我們也叫進(jìn)棧,也稱(chēng)壓?;蛉霔?。類(lèi)似子彈入彈夾。

棧的刪除操作我們也叫出棧,也稱(chēng)彈棧。如同彈夾中子彈出夾。

5.3 進(jìn)棧出棧變化形式

有些人受到固定思維,意味棧中最先進(jìn)棧的元素一定是最后出棧的,其實(shí)不一定。比如123依次入棧,那會(huì)有很多種結(jié)果。

  • 第一種:1、2 、3進(jìn),再1 、2 、3出。
  • 第二種:1進(jìn)1出2進(jìn)2出3進(jìn)3出。
  • 第三種:1進(jìn)2進(jìn)2出1出3進(jìn)3出。
  • 第四種:1進(jìn)1出2進(jìn)3進(jìn)3出2出。
  • 第五種:1進(jìn)2進(jìn)2出3進(jìn)3出1出。

從這里我們可以看出,三個(gè)元素就有5種變化,那么當(dāng)元素?cái)?shù)目變多時(shí),變化就有可能更多。在考研或者其他面試中,最喜歡的就是考查棧的合法出棧順序,所以這里一定要搞清楚。

5.4 棧的抽象數(shù)據(jù)類(lèi)型

前面說(shuō)過(guò),棧是一種特殊的線(xiàn)性表,所以線(xiàn)性表中有的操作棧都有。需要注意的是,前面我們?cè)谡務(wù)摶静僮鞯拿Q(chēng)寫(xiě)法時(shí)說(shuō)過(guò)名稱(chēng)一定要能讓人看懂。在嚴(yán)蔚敏一版的教材中,我們采用如下抽象數(shù)據(jù)結(jié)構(gòu)。

ADT 棧(stack) Data同線(xiàn)性表。元素具有相同的類(lèi)型,相鄰元素具有前驅(qū)和后繼關(guān)系。 OperationInitStack(*S):初始化操作,建立一個(gè)空棧s。DestroyStack(*S):若棧存在,則銷(xiāo)毀它。ClearStack(*S):將棧清空。StackEmpty(S):若棧為空,返回true,否咋返回false。GetTop(S,*e):若棧存在且非空,用e返回s的棧頂元素。Push(*S,e):若棧S存在,插入新元素e則棧S中并成為棧頂元素。Pop(*S,e):刪除棧S中棧頂元素,并用e返回其值。StackLength(S):返回棧S的元素個(gè)數(shù)。 endADT

5.5 順序棧

5.5.1 順序棧的定義

#define MaxSize 10 //定義棧中元素的最大個(gè)數(shù) typedef struct{ElemType data[MaxSize]; //靜態(tài)數(shù)組存放棧中元素int top; //棧頂指針 }SqStack;

在棧的定義中,我們用了top來(lái)指明棧頂元素在數(shù)組中的位置,top就如同老師用的游標(biāo)卡尺中的游標(biāo)一般,其可以來(lái)回移動(dòng),但是游標(biāo)不能超過(guò)尺,即棧頂指針不能超過(guò)定義的數(shù)組容量。一般來(lái)說(shuō),如果空棧,意味著棧中沒(méi)有元素,此時(shí)我們的top = -1。

在這里要科普一些關(guān)于棧頂指針和棧的一些知識(shí)。

棧頂指針也叫堆棧指針,一般堆棧的棧底不能動(dòng),所以數(shù)據(jù)入棧前要先修改堆棧指針,使它指向新的空余空間然后再把數(shù)據(jù)存進(jìn)去,出棧的時(shí)候相反。

堆棧指針,隨時(shí)跟蹤棧頂?shù)刂?#xff0c;按"先進(jìn)后出"的原則存取數(shù)據(jù)。

計(jì)算機(jī)中的堆棧主要用來(lái)保存臨時(shí)數(shù)據(jù),局部變量和中斷/調(diào)用子程序程序的返回地址。

ARM處理器中通常將寄存器R13作為堆棧指針(SP)。ARM處理器針對(duì)不同的模式,共有 6 個(gè)堆棧指針(SP),其中用戶(hù)模式和系統(tǒng)模式共用一個(gè)SP,每種異常模式都有各自專(zhuān)用的R13寄存器(SP)。它們通常指向各模式所對(duì)應(yīng)的專(zhuān)用堆棧,也就是ARM處理器允許用戶(hù)程序有六個(gè)不同的堆??臻g。這些堆棧指針?lè)謩e為R13、R13_svc、R13_abt、R13_und、R13_irq、R13_fiq。

5.5.2 初始化以及判空

如果要對(duì)棧進(jìn)行初始化,根據(jù)上面說(shuō)過(guò)top=-1為空棧的情況,我們可以寫(xiě)出如下所示的初始化代碼:

void InitStack(SqStack &S) {S.top = -1; }

根據(jù)初始化的特點(diǎn),如果我們想要判斷棧是否為空,只需:

bool StackEmpty(SqStack S) {if(S.top==-1)return true;elsereturn false; }

5.5.3 進(jìn)棧操作

對(duì)于進(jìn)棧操作push,我們可以:

bool Push(SqStack &S,ElemType x) {if(S.top == MaxSize-1) //棧滿(mǎn),報(bào)錯(cuò)return false;S.data[++S.top] = x; //移動(dòng)堆棧指針,并且給堆棧指針指向的棧頂元素賦值return true; }

5.5.4 出棧操作

對(duì)于出棧操作pop,我們可以:

bool Pop(SqStack &S,ElemType &x) {if(S.top==-1)return false;x = S.data[S.top--]; //先返回元素給x,再移動(dòng)堆棧指針return true; }

這里要注意的是,我們并沒(méi)有把出棧元素的數(shù)據(jù)清空,所以雖然出棧,但是只是邏輯上被刪除了,實(shí)際上數(shù)據(jù)還殘留在內(nèi)存中。

5.5.5 讀棧操作

對(duì)于讀棧,一般我們都是讀棧頂元素。如下所示:

bool GetTop(SqStack S,ElemType &x) {if(S.top == -1)return false;x = S.data[S.top];return true; }

5.5.6 關(guān)于棧頂指針的指向問(wèn)題

有時(shí)候,棧頂指針會(huì)在初始化的時(shí)候不是指向-1而是指向0,而當(dāng)棧滿(mǎn)的時(shí)候不是指向棧頂元素而是指向棧頂元素的下一位,此時(shí)上面的判空、初始化、讀棧、進(jìn)棧操作和出棧操作中代碼都會(huì)有些小小的不一樣,需要大家注意,這在考試中可能會(huì)出現(xiàn)。

5.5.7 共享?xiàng)?/h3>

顧名思義,共享?xiàng)>褪莾蓚€(gè)棧共用一個(gè)??臻g。

在順序棧的設(shè)計(jì)過(guò)程中,我們可以發(fā)現(xiàn)其用的是靜態(tài)數(shù)組來(lái)分配的棧空間,這就導(dǎo)致可能棧空間內(nèi)存不足,如果要使用動(dòng)態(tài)數(shù)組的方式去分配內(nèi)存,這么做很麻煩。

但是兩個(gè)棧就不一樣了,我們可以把兩個(gè)棧用一個(gè)很大的數(shù)組放著,其中這個(gè)數(shù)組是兩個(gè)棧的共享?xiàng)?臻g。如果你用的多,我用的少,那我就多給你一點(diǎn)空間,這樣很容易協(xié)調(diào)空間的分配。

兩個(gè)棧的空間放置如下,我們可以看成是兩個(gè)棧頂面對(duì)面放置。

兩個(gè)棧的棧指針移動(dòng)時(shí)如下所示:

共享?xiàng)4a如下:

//定義共享?xiàng)?#define MaxSize 10 typedef struct {ElemType data[MaxSize];int top0;int top1; }shstack;//初始化共享?xiàng)?void InitStack(ShStack &S) {S.top0 = -1;S.top1 = MaxSize; }

如果棧滿(mǎn)了,其條件是兩指針對(duì)應(yīng)的棧誰(shuí)再進(jìn)棧一次都會(huì)重合。

top0 +1 == top1

5.6 鏈棧

5.6.1 概述

我們把棧的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)叫做鏈棧。

相對(duì)于鏈棧來(lái)說(shuō),我們需要考慮第一個(gè)問(wèn)題:

我們要把棧頂放在鏈表的頭部還是尾部。

對(duì)于鏈表來(lái)說(shuō),鏈表不管是帶頭結(jié)點(diǎn)還是不帶頭結(jié)點(diǎn),都帶有一個(gè)頭指針,如果棧頂不處于鏈表頭部的話(huà),那么我們就要再聲明一個(gè)指針作為棧頂指針,這顯然很麻煩。既然這樣,我們不如把棧頂指針和頭指針合二為一即可。需要注意的是,合二為一的前提是棧頂實(shí)際上就是第一個(gè)結(jié)點(diǎn),如果你帶頭結(jié)點(diǎn),那么第一個(gè)結(jié)點(diǎn)就不能放元素了,這不太符合棧的定義了,所以在鏈棧中,我們一般不會(huì)再用到學(xué)習(xí)單鏈表所用到的頭結(jié)點(diǎn)了。

對(duì)于鏈棧,就不存在順序棧那樣空間不足的問(wèn)題了,除非是內(nèi)存不足。

5.6.2 鏈棧的定義

鏈棧中的定義在不同的書(shū)上有不同的方式,如果你想記錄棧的長(zhǎng)度,那么我推薦你使用下面的方式:

//結(jié)點(diǎn)定義 typedef struct StackNode {int data;StackNode* next; }StackNode,*LinkStackptr;//鏈棧定義 typedef struct LinkStack {LinkStackptr top;//指針int Length; }LinkStack

5.6.3 鏈棧的初始化

對(duì)于鏈棧初始化,就是把傳入的鏈棧指向空,并且設(shè)置棧長(zhǎng)為0。

//初始化 bool LinkStackInit(LinkStack &S) {S.top = NULL;S.Length = 0;return true; }

5.6.4 鏈棧的進(jìn)棧

對(duì)于鏈棧的進(jìn)棧,考慮帶頭結(jié)點(diǎn)的情況,我們可以發(fā)現(xiàn)實(shí)際上就是指定只能在頭結(jié)點(diǎn)后插,這樣的話(huà),根據(jù)之前在單鏈表學(xué)過(guò)的后插操作,可以自行寫(xiě)出代碼,這里不再贅述。

但是如果是不帶頭結(jié)點(diǎn),那么我們可以如下所示:

//進(jìn)棧 bool push(LinkStack &S, int e) { //生成新節(jié)點(diǎn)并且把添加值加入StackNode *newnode = new StackNode;newnode->data = e;//開(kāi)始添加結(jié)點(diǎn),變換指針順序newnode->next = S.top;S.top = newnode;//添加完成,計(jì)入表長(zhǎng)S.Length++;return true; }

5.6.5 鏈表的出棧

對(duì)于不包含頭結(jié)點(diǎn)鏈表的出棧,無(wú)非就是對(duì)棧頂指針后的結(jié)點(diǎn)刪除,需要注意的是當(dāng)棧中結(jié)點(diǎn)剩余0和1的時(shí)候,我們需要設(shè)置額外情況。

//彈棧 bool pop(LinkStack& S, int& e) {if (S.Length == 0) {return false;}//生成指針用于記錄刪除結(jié)點(diǎn)所在地址StackNode *p = new StackNode;//返回刪除結(jié)點(diǎn)中的值e = S.top->data;//將指針移向棧頂p = S.top;//棧頂位置改變S.top = S.top->next;//釋放原棧頂delete(p);//表長(zhǎng)改變S.Length--;return false; }

5.6.6 輸出鏈棧

輸出鏈棧也很容易,只需指定一根指針從棧頂掃向棧底即可。掃的時(shí)候依次輸出每個(gè)結(jié)點(diǎn)中的數(shù)據(jù)。

bool CoutStack(LinkStack S) {StackNode* p = S.top;cout << "由棧頂?shù)綏5?#xff1a;" ;while (p) {cout << p->data << endl;p = p->next;}cout << endl;return true; }

5.6.7 輸出棧頂元素

//輸出棧頂元素 bool GetTop(LinkStack& S, int e) {if (S.Length == 0)return false;e = S.top->data;return true; }

5.7 鏈棧和順序棧的對(duì)比

對(duì)比順序棧和鏈棧,它們的時(shí)間復(fù)雜度一樣,均為O(1)。對(duì)于空間性能,順序棧的空間是死的,是固定的;而對(duì)于鏈棧,雖然空間沒(méi)有限制,但是也可能面臨內(nèi)存不足的問(wèn)題。

總結(jié)

以上是生活随笔為你收集整理的数据结构杂谈(五)——栈的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。