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

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

生活随笔

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

编程问答

栈和队列OJ练习——栈实现队列,队列实现栈

發(fā)布時(shí)間:2023/12/20 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 栈和队列OJ练习——栈实现队列,队列实现栈 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • ?棧實(shí)現(xiàn)隊(duì)列
      • 🥝雙棧數(shù)據(jù)倒入法
        • 🌠隊(duì)列“壓棧”
        • 🌠棧的“出隊(duì)”
        • 🌠棧"取隊(duì)頭"
        • 🌠遍歷雙棧“隊(duì)列”
        • 🌠棧的隊(duì)列判空和釋放
  • ?隊(duì)列實(shí)現(xiàn)棧
      • 🥝雙隊(duì)列結(jié)點(diǎn)遷移法
        • 🌠隊(duì)列“壓棧”
        • 🌠隊(duì)列“彈棧”
        • 🌠隊(duì)列取“棧頂”
        • 🌠雙隊(duì)列的棧中元素個(gè)數(shù)
        • 🌠雙隊(duì)列的棧遍歷
  • ?后話

?棧實(shí)現(xiàn)隊(duì)列

棧與隊(duì)列的數(shù)據(jù)存儲(chǔ)方式完全不同,棧的數(shù)據(jù)遵循先進(jìn)后出模式FILO,而隊(duì)列為先進(jìn)先出模式FIFO,要想使用棧的結(jié)構(gòu)實(shí)現(xiàn)隊(duì)列的數(shù)據(jù)增刪模式,需要使用棧的性質(zhì)并對(duì)其稍加巧用,就可以達(dá)到同隊(duì)列的數(shù)據(jù)存儲(chǔ)訪問(wèn)相同的效果。

注意,本章中用棧實(shí)現(xiàn)隊(duì)列所用到的棧函數(shù),以及隊(duì)列實(shí)現(xiàn)棧使用到的隊(duì)列接口函數(shù)都在上一章模擬實(shí)現(xiàn)提及到,詳情請(qǐng)參照上一章,鏈接在此數(shù)據(jù)結(jié)構(gòu)——棧和隊(duì)列_VelvetShiki_Not_VS的博客-CSDN博客。


🥝雙棧數(shù)據(jù)倒入法

定義兩個(gè)棧,一個(gè)用于臨時(shí)存放壓棧的數(shù)據(jù),命名其為Push棧,再定義一個(gè)專(zhuān)用于出數(shù)據(jù)的棧Pop,當(dāng)數(shù)據(jù)壓入時(shí),全部壓入Push棧,只要遇到出棧命令,就將Push棧的所有數(shù)據(jù)取頂并全部倒入Pop棧中,此時(shí)的Pop棧數(shù)據(jù)為Push棧數(shù)據(jù)的逆序,即如果Push棧的數(shù)據(jù)壓入為1,2,3,4,分別取頂為4,3,2,1并壓入Pop棧,此時(shí)Pop棧由棧頂自棧底的數(shù)據(jù)依次為1,2,3,4,如果全部出棧則剛好與數(shù)據(jù)的入棧順序相同(即入棧1,2,3,4,出棧也為1,2,3,4的順序)。

🎀雙棧指針的結(jié)構(gòu)定義

棧的基本結(jié)構(gòu)定義遵循順序表的定義方式,包含存儲(chǔ)數(shù)據(jù)的數(shù)值域,標(biāo)識(shí)棧頂下標(biāo)的整型top,標(biāo)識(shí)棧容量的整型capacity,相關(guān)棧函數(shù)接口可參考上述棧和隊(duì)列鏈接,本章的棧轉(zhuǎn)換隊(duì)列算法引用的棧接口函數(shù)不再詳細(xì)贅述。

//棧定義 typedef int STEtype; typedef struct Stack {STEtype* arr; //棧通過(guò)順序表實(shí)現(xiàn),定義可擴(kuò)容數(shù)組,容量和頂部int top;int capacity; }ST;//兩個(gè)棧底指針的結(jié)構(gòu)體 typedef struct MyQueue {ST* Push; //Push棧用于數(shù)據(jù)壓入的臨時(shí)存放ST* Pop; //當(dāng)需要出隊(duì)時(shí),將Push棧的數(shù)據(jù)取頭倒入Pop棧,并逐個(gè)取頭出隊(duì) }MQ;
  • 定義包含指向兩個(gè)棧空間的指針Push和Pop,前者用于臨時(shí)存放壓入的待出棧數(shù)據(jù),后者用于出棧,注意兩個(gè)棧都需要初始化,且占用的內(nèi)存空間均不相同,指針包含在結(jié)構(gòu)體內(nèi)只是為了便于管理和解引用。
  • 🎀棧指針結(jié)構(gòu)體初始化函數(shù)

    MQ* StructInit() //初始化棧指針 {MQ* Stacks = (MQ*)malloc(sizeof(MQ));Stacks->Push = StackInit();Stacks->Pop = StackInit();return Stacks; }
  • 棧的初始化函數(shù)StackInit包含了棧結(jié)構(gòu)體信息的內(nèi)存空間開(kāi)辟,棧指針和數(shù)據(jù)的置空。因?yàn)樵撍惴ㄉ婕皟蓚€(gè)棧空間的開(kāi)辟和使用,初始化時(shí)也需要將兩個(gè)棧指針指向的棧空間都初始化,以便后續(xù)其他函數(shù)的使用。
  • 🌠隊(duì)列“壓棧”

    在隊(duì)列一章我們?cè)褂面湵淼姆绞綄?shí)現(xiàn)隊(duì)列數(shù)據(jù)的入隊(duì),而使用順序表的方式同樣可是實(shí)現(xiàn)入隊(duì)操作,此處因?yàn)樾枋褂脳5目臻g結(jié)構(gòu)實(shí)現(xiàn)隊(duì)列功能,實(shí)質(zhì)上也使用了順序表的方式入隊(duì)。對(duì)于隊(duì)列的“壓棧”操作,只需根據(jù)需求將需要入隊(duì)的數(shù)據(jù)全部壓到Push棧中即可,原理圖如下:

    🎀隊(duì)列壓棧函數(shù)

    void Push(MQ* obj, STEtype x) {assert(obj);StackPush(obj->Push, x); //調(diào)用了棧自身的壓棧函數(shù) }

    🌠棧的“出隊(duì)”

    使用棧入隊(duì)與壓棧的方式大體相同,因?yàn)椴恍枰獙?duì)數(shù)據(jù)進(jìn)行過(guò)多的操作,直接將數(shù)據(jù)存入即可,如果此時(shí)直接從Push棧里將數(shù)據(jù)彈出,就是棧的出棧方式。但如果想要模擬隊(duì)列的出隊(duì)方式,需要對(duì)棧的彈出做些文章,原理圖如下:

  • 對(duì)Push棧壓入部分?jǐn)?shù)據(jù)(即入隊(duì)操作)后,執(zhí)行出隊(duì)命令,此時(shí)不能將Push棧的數(shù)據(jù)直接彈出,而應(yīng)該全部倒入Pop棧中,具體倒入的方式為將Push中的數(shù)據(jù)逐個(gè)取頂后,將該數(shù)壓入Pop棧,則最后進(jìn)入Push棧中的數(shù)據(jù)挪到了Pop底部,而最先壓入Push占中的數(shù)據(jù)則挪到了Pop頂部(Push棧頂->Pop棧底,Push棧底->Pop棧頂),也即逆序了Push棧中的數(shù)據(jù),逆序完成后從Pop棧出隊(duì),即為與壓入Push棧的數(shù)據(jù)順序相同的出棧順序,完成了隊(duì)列的出隊(duì)模擬。
  • 🎀棧出隊(duì)函數(shù)

    void Pop(MQ* obj) {assert(obj); //檢查兩個(gè)棧底指針有效性if (StackEmpty(obj->Pop)) //如果Pop棧不為空,則執(zhí)行Push棧中數(shù)據(jù)的倒入{while (!StackEmpty(obj->Push)) //如果Push棧不為空,則繼續(xù)將數(shù)據(jù)取頂?shù)谷隤op,再將Push頂依次彈棧{StackPush(obj->Pop, StackTop(obj->Push));StackPop(obj->Push);}}StackPop(obj->Pop); //將位于Pop棧頂元素彈出 }
  • 觀察代碼,可以發(fā)現(xiàn)數(shù)據(jù)從Push棧倒入Pop棧過(guò)程僅有在Pop棧為空的情況下才能執(zhí)行,這是因?yàn)橄葔喝隤ush棧的數(shù)據(jù)應(yīng)該先從Pop出棧,而后入的數(shù)據(jù)則只能跟在未完成出棧的數(shù)據(jù)后面。如果Pop棧中的數(shù)據(jù)仍未出完,此時(shí)就將Push棧中的數(shù)據(jù)倒過(guò)去,則仍未出隊(duì)的更早的數(shù)據(jù)就會(huì)更晚出隊(duì),不符合隊(duì)列性質(zhì),可由如下原理圖解釋:
  • 🎃一定要記住,要符合棧的隊(duì)列出隊(duì)性質(zhì),數(shù)據(jù)的入隊(duì)只能從Push棧一端進(jìn)入,而數(shù)據(jù)的出隊(duì)只能從Pop棧一端彈出。

    🌠棧"取隊(duì)頭"

    對(duì)于雙棧結(jié)構(gòu)的隊(duì)列取隊(duì)頭元素,就是取非空Pop棧的棧頂元素,如果僅有Pop棧為空,Push棧非空,則從Push棧元素全部取頂壓入Pop棧再取頂(Pop不彈棧);如果兩棧均空,則由棧取頂函數(shù)StackTop中判斷棧為空,返回?zé)o意義值-1。

    🎀棧取隊(duì)頭函數(shù)

    STEtype Peek(MQ* obj) //取隊(duì)頭元素 {assert(obj);if (StackEmpty(obj->Pop)){while (!StackEmpty(obj->Push)){StackPush(obj->Pop, StackTop(obj->Push));StackPop(obj->Push);}}return StackTop(obj->Pop); }

    🌈測(cè)試用例

    MQ* MyQ = StructInit(); printf("%d", Peek(MyQ)); Push(MyQ, 1); printf("隊(duì)頭元素為:%d\n", Peek(MyQ)); Push(MyQ, 2); printf("隊(duì)頭元素為:%d\n", Peek(MyQ)); Push(MyQ, 3); printf("隊(duì)頭元素為:%d\n", Peek(MyQ)); Push(MyQ, 4); printf("隊(duì)頭元素為:%d\n", Peek(MyQ)); Pop(MyQ); printf("隊(duì)頭元素為:%d\n", Peek(MyQ));

    觀察結(jié)果

    🌠遍歷雙棧“隊(duì)列”

    對(duì)于棧和隊(duì)列的結(jié)構(gòu)已知,如果要對(duì)這兩種特殊的線性結(jié)構(gòu)進(jìn)行數(shù)值遍歷,則需要清空棧或隊(duì)列的元素,將元素全部彈棧或出隊(duì),這樣遍歷完成后棧或隊(duì)列均為空。對(duì)于使用棧結(jié)構(gòu)模擬的隊(duì)列亦是如此。

    🎀遍歷函數(shù)

    void PrintQueue(MQ* obj) {assert(obj);printf("Head-> ");while (!StackEmpty(obj->Pop)) //如果Pop棧不為空,將Pop棧元素循環(huán)取頂并彈棧{printf("%d ", Peek(obj));Pop(obj);}if (!StackEmpty(obj->Push)) //此時(shí)Pop棧一定為空,再判斷Push棧是否為空{Peek(obj); //如果非空,則將Push棧中元素循環(huán)取頂并壓入Pop棧while (!StackEmpty(obj->Pop)){printf("%d ", Peek(obj)); //再將從Push棧中壓入Pop棧的數(shù)據(jù)依次取頂并彈棧Pop(obj);}}printf("<-Tail\n"); }

    🌈測(cè)試用例

    //雙棧初始化為空 MQ* MyQ = StructInit(); //入隊(duì)&出隊(duì) Push(MyQ, 1); Push(MyQ, 2); Push(MyQ, 3); Pop(MyQ); Push(MyQ, 4); Push(MyQ, 5); Push(MyQ, 6); Push(MyQ, 7); Pop(MyQ); //兩次隊(duì)列遍歷 PrintQueue(MyQ); PrintQueue(MyQ);

    🌈觀察結(jié)果

    Head-> 3 4 5 6 7 <-Tail Head-> <-Tail

    🌠棧的隊(duì)列判空和釋放

    當(dāng)兩個(gè)棧均為空時(shí),雙棧構(gòu)成的隊(duì)列才為空。

    🎀判空函數(shù)

    bool Empty(MQ* obj) //判斷隊(duì)列是否為空 {assert(obj);return StackEmpty(obj->Push) && StackEmpty(obj->Pop); }

    釋放雙棧構(gòu)成的隊(duì)列時(shí),需要將先前開(kāi)辟過(guò)的所有內(nèi)存空間均釋放,這些空間包括:

  • Push棧和Pop棧初始化開(kāi)辟和擴(kuò)容的數(shù)組空間arr
  • 指向雙棧的指針Push和Pop
  • 包含雙棧指針的結(jié)構(gòu)體信息指針obj
  • 🎀雙棧的隊(duì)列釋放函數(shù)

    MQ* Free(MQ* obj) //銷(xiāo)毀隊(duì)列 {assert(obj); //檢查雙棧指針有效性obj->Push = StackDestroy(obj->Push); //分別釋放兩個(gè)棧,順序可以顛倒obj->Pop = StackDestroy(obj->Pop);free(obj); //再釋放雙棧結(jié)構(gòu)體信息指針,置空后返回obj = NULL;return obj; }

    🌈調(diào)試觀察結(jié)果


    ?隊(duì)列實(shí)現(xiàn)棧

    在前一章中,我們使用了鏈表的方式實(shí)現(xiàn)了隊(duì)列的基本結(jié)構(gòu),而使用隊(duì)列的結(jié)構(gòu)來(lái)實(shí)現(xiàn)棧,其思維邏輯與棧實(shí)現(xiàn)隊(duì)列的大體相同,都是需要進(jìn)行數(shù)據(jù)的倒入和交換。因?yàn)樾枰褂玫疥?duì)列的結(jié)構(gòu),所以隊(duì)列的基本函數(shù)接口也可以參考前章中如上文的棧和隊(duì)列鏈接,而本課題在此基礎(chǔ)上實(shí)現(xiàn)對(duì)棧的數(shù)據(jù)存儲(chǔ)和訪問(wèn)先進(jìn)后出(FILO)的模擬。

    🥝雙隊(duì)列結(jié)點(diǎn)遷移法

    使用雙棧的隊(duì)列模擬實(shí)現(xiàn),使用了兩個(gè)棧Push和Pop棧分別實(shí)現(xiàn)入隊(duì)與數(shù)據(jù)倒入Pop棧再取頂彈出的方式實(shí)現(xiàn)出隊(duì)操作。而要使用隊(duì)列模擬棧的數(shù)據(jù)存儲(chǔ)和訪問(wèn)方式,也需要使用到兩個(gè)隊(duì)列,分別進(jìn)行結(jié)點(diǎn)數(shù)據(jù)的入隊(duì)和出隊(duì),整體思路大致如下圖所示:

    🎃可以看出,雙隊(duì)列的“壓棧”操作與隊(duì)列的入隊(duì)幾乎沒(méi)有區(qū)別,都是將數(shù)據(jù)直接入隊(duì)“壓棧”即可,而在“彈棧”過(guò)程中,隊(duì)列將數(shù)據(jù)的出隊(duì)為了滿足棧的性質(zhì),即最先存儲(chǔ)的數(shù)據(jù)最后出隊(duì),而最后存儲(chǔ)的數(shù)據(jù)反而最先出隊(duì),所以需要將隊(duì)列最后入隊(duì)的那個(gè)數(shù)據(jù)出隊(duì)即可。但根據(jù)隊(duì)列的性質(zhì),出隊(duì)操作僅能對(duì)隊(duì)頭元素彈出,所以將一個(gè)非空隊(duì)列中最后一個(gè)結(jié)點(diǎn)元素前面的所有結(jié)點(diǎn)都依次入隊(duì)到另一個(gè)隊(duì)列,再將最后一個(gè)元素出隊(duì),即可滿足要求。

    🎀雙隊(duì)列結(jié)構(gòu)體模擬棧

    typedef int QEtype; typedef struct Queue //基于鏈表結(jié)構(gòu)的隊(duì)列結(jié)構(gòu)體定義 {QEtype data;struct Queue* next; }QE;//指向兩個(gè)隊(duì)列指針的指針 typedef struct MyStack {QE* Q1; //指向第一個(gè)隊(duì)列QE* Q2; //指向第二個(gè)隊(duì)列 }MST;
  • 因?yàn)橐瑫r(shí)控制兩個(gè)隊(duì)列的操作,所以在定義隊(duì)列結(jié)構(gòu)的基礎(chǔ)上,還需再定義指向兩個(gè)隊(duì)列的頭結(jié)點(diǎn)指針?lè)奖憬y(tǒng)一管理。隊(duì)列因?yàn)槭且枣湵頌榛A(chǔ)定義,所以無(wú)需初始化,插入數(shù)據(jù)時(shí)直接開(kāi)辟新結(jié)點(diǎn)入隊(duì)數(shù)據(jù)即可。
  • 🎀雙隊(duì)列指針初始化函數(shù)

    MST* StackCreate() {MST* Queues = (MST*)malloc(sizeof(MST)); //開(kāi)辟包含隊(duì)列指針信息的結(jié)構(gòu)體空間初始化Queues->Q1 = Queues->Q2 = NULL; //將兩個(gè)指向Q1和Q2隊(duì)列的指針初始化置空return Queues; }
  • 有了初始化函數(shù),與空間和指針初始化函數(shù)相對(duì)應(yīng)的是隊(duì)列空間和指針的銷(xiāo)毀函數(shù)。
  • 🎀雙隊(duì)列指針?shù)N毀函數(shù)

    MST* Free(MST* obj) {assert(obj);QueueDestroy(&obj->Q1); //將隊(duì)列Q1和Q2銷(xiāo)毀,即鏈表中所有結(jié)點(diǎn)的釋放,并將隊(duì)列指針置空QueueDestroy(&obj->Q2);free(obj); //將指向隊(duì)列指針信息的結(jié)構(gòu)體形參指針釋放并置空,并返回給實(shí)參obj = NULL;return obj; }
  • 因?yàn)槭褂玫氖莾蓚€(gè)隊(duì)列對(duì)棧的模擬實(shí)現(xiàn),所以如果對(duì)該棧結(jié)構(gòu)判空。
  • 🎀雙隊(duì)列判空函數(shù):

    bool Empty(MST* obj) {assert(obj);return QueueEmpty(&(obj->Q1)) && QueueEmpty(&(obj->Q2)); }

    🌠隊(duì)列“壓棧”

    從開(kāi)頭的原理圖可看出,對(duì)于隊(duì)列的壓棧操作與入隊(duì)基本一致,因?yàn)槭腔阪湵韺?duì)數(shù)據(jù)的存儲(chǔ),所以只需向非空隊(duì)列的一端入隊(duì)數(shù)據(jù)即可。

    void Push(MST* obj, QEtype x) {assert(obj);if (QueueEmpty(&obj->Q1)) //判斷Q1是否為空,如果為空則保持指針指向不變,如果非空,則此時(shí)Q2一定為空,指針交換指向{QueuePush(&obj->Q2, x); //將新增數(shù)據(jù)入隊(duì)到非空隊(duì)列中(可能是Q1隊(duì)列,也有可能是Q2隊(duì)列)}else{QueuePush(&obj->Q1, x); } }

    🎃需要注意的是,使用雙隊(duì)列結(jié)構(gòu)入隊(duì),入隊(duì)的一端永遠(yuǎn)是非空隊(duì)列,而另一個(gè)隊(duì)列一定為空。不能對(duì)空隊(duì)列進(jìn)行入隊(duì)操作,而空隊(duì)列與非空隊(duì)列不能確定具體是Q1或是Q2,因?yàn)樵诓粩嗟娜腙?duì)與出隊(duì)過(guò)程中,兩個(gè)隊(duì)列都分別可能對(duì)空隊(duì)列或非空隊(duì)列,但入隊(duì)操作僅能在非空隊(duì)列的一方進(jìn)行。如果初始兩個(gè)隊(duì)列均為空,依據(jù)上述函數(shù)作if判斷,當(dāng)Q1為空時(shí),默認(rèn)Q2為非空隊(duì)列(即使Q2隊(duì)列本身也為空),向其中入隊(duì)數(shù)據(jù)即可。

    🌠隊(duì)列“彈棧”

    隊(duì)列出隊(duì)要遵循棧的彈棧規(guī)則,就必須讓隊(duì)列進(jìn)行結(jié)點(diǎn)數(shù)據(jù)的遷移,讓非空隊(duì)列的隊(duì)尾結(jié)點(diǎn)數(shù)據(jù)進(jìn)行單獨(dú)的出隊(duì)“彈棧”。

    🎀雙隊(duì)列彈棧函數(shù)

    void Pop(MST* obj) {assert(obj);if (Empty(obj)) //如果雙隊(duì)列均為空,則無(wú)需出隊(duì){return;}QE** Empty = &(obj->Q1), ** NonEmpty = &(obj->Q2); //將兩個(gè)隊(duì)列指針取地址,分別賦值給定義的二級(jí)指針空和非空if (!QueueEmpty(Empty)) //如果空指針指向隊(duì)列不為空,則交換兩個(gè)指針指向{Empty = &(obj->Q2);NonEmpty = &(obj->Q1);}while (QueueSize(NonEmpty) > 1) //將非空隊(duì)列的數(shù)據(jù)除隊(duì)末結(jié)點(diǎn)元素外,全部壓入空隊(duì)列一方{QueuePush(Empty, QueueTop(NonEmpty));QueuePop(NonEmpty);}QueuePop(NonEmpty); //最后將僅留下一個(gè)結(jié)點(diǎn)的原非空隊(duì)末元素出隊(duì),同時(shí)此隊(duì)列變?yōu)榭贞?duì)列 }
  • 如果對(duì)一個(gè)由雙隊(duì)列構(gòu)成的棧結(jié)構(gòu)進(jìn)行彈棧,通過(guò)對(duì)兩個(gè)隊(duì)列的頭結(jié)點(diǎn)地址進(jìn)行判空檢測(cè),如果均為空則拒絕執(zhí)行彈棧,雙隊(duì)列判空函數(shù)Empty()已由上述給出。
  • 如果該棧不為空,先假設(shè)Q1為空隊(duì)列,Q2為非空隊(duì)列,為了驗(yàn)證假設(shè)是否正確,需要對(duì)被假設(shè)為空隊(duì)列的Q1進(jìn)行鏈表判空檢測(cè),如果驗(yàn)證Q1為空則不需要更改,否則需要與Q2交換頭銜便于后續(xù)操作。此處需要定義兩個(gè)指向隊(duì)列指針的二級(jí)指針Empty和NonEmpty,用于規(guī)定和矯正Q1與Q2的空與非空性質(zhì)。因?yàn)榭赡苄枰腝1指針為非空或空,所以取Q1和Q2的地址進(jìn)入判空判斷!QueueEmpty(Empty),當(dāng)原Empty指向的隊(duì)列指針Q1驗(yàn)證其所在隊(duì)列為非空時(shí),原假設(shè)證偽,需要與Q2指針交換指向,使空指針Empty改指向空隊(duì)列Q2,而原指向空隊(duì)列Q2的非空指針NonEmpty則需改指向非空隊(duì)列Q1,即完成了空與非空二級(jí)指針指向空與非空隊(duì)列一級(jí)指針的一一對(duì)應(yīng)。
  • 再次強(qiáng)調(diào),因?yàn)榭赡苄枰淖冴?duì)列的一級(jí)指針Q1和Q2的地址,所以必須通過(guò)取指針地址的方式更改指針指向,而不能傳入隊(duì)列指針本身以一級(jí)形參地址的方式修改(也可通過(guò)一級(jí)指針修改后通過(guò)返回值傳回,將新地址賦值于原指針)。
  • 當(dāng)指針糾正隊(duì)列指向后,需要進(jìn)行的就是對(duì)非空隊(duì)列結(jié)點(diǎn)的遷移,除了位于隊(duì)尾的末節(jié)點(diǎn),其余結(jié)點(diǎn)通過(guò)函數(shù)內(nèi)循環(huán)依次取頂并入隊(duì)于空隊(duì)列,每取頂入隊(duì)一次就出一次隊(duì),便于對(duì)隊(duì)列后續(xù)數(shù)據(jù)的順利訪問(wèn)。當(dāng)非空隊(duì)列僅剩隊(duì)尾最后一個(gè)結(jié)點(diǎn)時(shí),取頂與入隊(duì)出隊(duì)循環(huán)結(jié)束,將該結(jié)點(diǎn)作為棧頂元素單獨(dú)彈出,便完成了隊(duì)列的彈棧(原理圖已于開(kāi)頭給出)。
  • 如果數(shù)據(jù)在入隊(duì)與出隊(duì)之間交替,新增的數(shù)據(jù)也必須遵從值以非空隊(duì)列的一方入隊(duì),并且越晚入隊(duì)的數(shù)據(jù)越先彈出,以符合棧的數(shù)據(jù)結(jié)構(gòu)特征。
  • 原理圖如下

    🌠隊(duì)列取“棧頂”

    雙隊(duì)列結(jié)構(gòu)棧的取棧頂元素函數(shù)也需要尋找空與非空隊(duì)列,在隊(duì)列結(jié)構(gòu)中,非空隊(duì)列的隊(duì)尾元素是最后入隊(duì)的,所以該元素即為棧的棧頂元素,遍歷非空隊(duì)列并對(duì)隊(duì)尾元素取值返回即可。

    🎀雙隊(duì)列棧取頂函數(shù)

    QEtype Top(MST* obj) {assert(obj);if (Empty(obj)) //如果雙隊(duì)列均為空,則無(wú)棧頂元素可取{return NULL;}QE** Empty = &obj->Q1, ** NonEmpty = &obj->Q2; if (!QueueEmpty(Empty)) //重新分配空與非空指向隊(duì)列指針,原理與Pop函數(shù)交換一致 {Empty = &obj->Q2;NonEmpty = &obj->Q1;}QE* tail = *NonEmpty; //定義臨時(shí)遍歷找尾結(jié)點(diǎn)指針tail,對(duì)非空隊(duì)列進(jìn)行遍歷取尾while (tail->next){tail = tail->next;}return tail->data; //找到末節(jié)點(diǎn),返回結(jié)點(diǎn)數(shù)值域值,且不彈棧(不出隊(duì)) }

    🌠雙隊(duì)列的棧中元素個(gè)數(shù)

    🎀雙隊(duì)列的棧元素個(gè)數(shù)計(jì)算函數(shù)

    int Size(MST* obj) {assert(obj);if (Empty(obj)){return 0;}if (QueueEmpty(&obj->Q1)){return QueueSize(&obj->Q2);}return QueueSize(&obj->Q1); }

    🌠雙隊(duì)列的棧遍歷

    棧或隊(duì)列的遍歷都會(huì)清空其結(jié)構(gòu)的所有元素,只不過(guò)每次出隊(duì)或彈棧都會(huì)先將隊(duì)列取頂后再出隊(duì)。

    🎀遍歷函數(shù)

    void Print(MST* obj) {assert(obj);printf("Top-> ");while (!Empty(obj)){printf("%d ", Top(obj));Pop(obj);}printf("<-Bot\n"); }

    🌈測(cè)試用例1

    //棧的隊(duì)列指針初始化 MST* MyST = StackCreate(); //隊(duì)列壓棧 Push(MyST, 1); Push(MyST, 2); Push(MyST, 3); Push(MyST, 4); //遍歷打印和全部彈棧 Print(MyST); printf("棧頂元素為:%d\n", Top(MyST)); printf("棧中有%d個(gè)元素\n", Size(MyST)); printf("棧是否為空:%d\n", Empty(MyST));

    🌈結(jié)果觀察

    Top-> 4 3 2 1 <-Bot 棧頂元素為:-1 棧中有0個(gè)元素 棧是否為空:1

    🌈測(cè)試用例2

    MST* MyST = StackCreate(); Push(MyST, 1); Push(MyST, 2); Push(MyST, 3); Push(MyST, 4); printf("棧頂元素為:%d\n", Top(MyST)); Pop(MyST); Pop(MyST); printf("棧頂元素為:%d\n", Top(MyST)); Push(MyST, 5); Push(MyST, 6); Push(MyST, 7); printf("棧頂元素為:%d\n", Top(MyST)); Pop(MyST); Push(MyST, 8); Push(MyST, 9); printf("棧中有%d個(gè)元素\n", Size(MyST)); printf("棧是否為空:%d\n", Empty(MyST)); Print(MyST); printf("棧頂元素為:%d\n", Top(MyST)); printf("棧是否為空:%d\n", Empty(MyST));

    🌈結(jié)果觀察

    棧頂元素為:4 棧頂元素為:2 棧頂元素為:7 棧中有6個(gè)元素 棧是否為空:0 Top-> 9 8 6 5 2 1 <-Bot 棧頂元素為:-1 棧是否為空:1

    🌈測(cè)試用例3

    MST* MyST = StackCreate(); Push(MyST, 1); Push(MyST, 2); Push(MyST, 3); Push(MyST, 4); MyST = Free(MyST); Print(MyST);

    🌈結(jié)果觀察

    當(dāng)函數(shù)執(zhí)行到Free釋放時(shí),可看到實(shí)參和旗下指針已經(jīng)被全部釋放:

    所以當(dāng)空指針進(jìn)入Print打印遍歷函數(shù)時(shí),就會(huì)被指針斷言判空所截?cái)?#xff0c;程序終止。
    為空:%d\n", Empty(MyST));

    🌈結(jié)果觀察```c 棧頂元素為:4 棧頂元素為:2 棧頂元素為:7 棧中有6個(gè)元素 棧是否為空:0 Top-> 9 8 6 5 2 1 <-Bot 棧頂元素為:-1 棧是否為空:1

    🌈測(cè)試用例3

    MST* MyST = StackCreate(); Push(MyST, 1); Push(MyST, 2); Push(MyST, 3); Push(MyST, 4); MyST = Free(MyST); Print(MyST);

    🌈結(jié)果觀察

    當(dāng)函數(shù)執(zhí)行到Free釋放時(shí),可看到實(shí)參和旗下指針已經(jīng)被全部釋放:

    所以當(dāng)空指針進(jìn)入Print打印遍歷函數(shù)時(shí),就會(huì)被指針斷言判空所截?cái)?#xff0c;程序終止。


    ?后話

  • 博客項(xiàng)目代碼開(kāi)源,獲取地址請(qǐng)點(diǎn)擊本鏈接:棧和隊(duì)列OJ 云代碼倉(cāng)。
  • 若閱讀中存在疑問(wèn)或不同看法歡迎在博客下方或碼云中留下評(píng)論。
  • 本章練習(xí)題目來(lái)源:隊(duì)列實(shí)現(xiàn)棧,棧實(shí)現(xiàn)隊(duì)列
  • 歡迎訪問(wèn)我的Gitee碼云,如果對(duì)您有所幫助還可以一鍵三連,獲取更多學(xué)習(xí)資料請(qǐng)關(guān)注我,您的支持是我分享的動(dòng)力~
  • 總結(jié)

    以上是生活随笔為你收集整理的栈和队列OJ练习——栈实现队列,队列实现栈的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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