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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

我幼儿园的弟看了都直呼简单的【栈和队列】

發布時間:2023/12/8 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 我幼儿园的弟看了都直呼简单的【栈和队列】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 1. 棧
    • 1.1 棧的概念及結構
    • 1.2 實現棧
  • 2. 隊列
    • 2.1 隊列的概念及結構
    • 2.2 隊列的實現
  • 3. 概念選擇題
  • 4. 棧和隊列OJ題
    • 4.1 括號匹配問題
    • 4.2 用隊列實現棧
    • 4.3 用棧實現隊列
    • 4.4 設計循環隊列


1. 棧

1.1 棧的概念及結構

棧:一種特殊的線性表(在邏輯上是連續存儲的,物理上不一定連續),其只允許在固定的一端進行插入和刪除元素操作。
進行數據插入和刪除操作的一端稱為棧頂,另一端稱為棧底棧中的數據元素遵守后進先出LIFO(Last In First Out)的原則

例如上面在棧中的數據只能先依次拿出棧頂的數據,出棧的順序依此是4,3,2,1,入棧時是1,2,3,4,所以后進先出也可以理解為先進后出。

壓棧:棧的插入操作叫做進棧/壓棧/入棧,入數據在棧頂
出棧:棧的刪除操作叫做出棧,出數據也在棧頂

注意:這里的棧是數據結構里的棧,主要是在內存堆區中對數據進行管理。而不是操作系統中劃分內存區域中的棧,但是都有一個特點,都遵循后進先出。


1.2 實現棧

棧的實現一般可以使用數組或者鏈表實現,相對而言數組的結構實現更優一些。因為數組在尾上插入數據的代價比較小,并且數組的地址是連續的,訪問起來緩存的利用率也會更高一些。

但因為數組并不是隨時動態開辟的空間,如果數組元素滿了需要考慮擴容,一次擴容原來的2倍,勢必會有一些空間上的浪費。
如果比較在意空間,可以使用帶頭雙向鏈表,這是比較方便的,如果使用單鏈表,要把鏈表的頭結點作為棧頂,尾結點作為棧底,因為鏈表的頭插頭刪比較高效。

代碼實現:

//函數聲明 #pragma once#include <stdio.h> #include <assert.h> #include <stdlib.h> #include <stdbool.h>//創建棧 typedef int DATATYPE; typedef struct Stack {DATATYPE* stack;int top;int capacity; }Stack;//初始化棧 void InintStack(Stack* ptrs);//入棧 void PushStack(Stack* ptrs, DATATYPE data);//出棧 void PopStack(Stack* ptrs);//訪問棧頂數據 DATATYPE StackTop(Stack* ptrs);//判斷棧是否為空 bool StackEmpty(Stack* ptrs);//數據個數 int StackSize(Stack* ptrs);//銷毀棧 void DestroyStack(Stack* ptrs);

注意:不要直接訪問結構體中的數據,即使非常簡單也要封裝為一個函數來訪問。

//函數實現 #define _CRT_SECURE_NO_WARNINGS 1 #include "StackQueue.h"//初始化棧 void InintStack(Stack* ptrs) {assert(ptrs);ptrs->stack = NULL;//注意如果初始話top為0//那么棧頂的元素下標時top-1ptrs->top = ptrs->capacity = 0; }//檢查容量 void checkSys(Stack* ptrs) { if (ptrs->capacity == ptrs->top){int newCapacity = ptrs->capacity == 0 ? sizeof(DATATYPE) : 2 * ptrs->capacity;DATATYPE* ret = (DATATYPE*)realloc(ptrs->stack, sizeof(DATATYPE) * newCapacity);if (!ret){perror("calloc fali");exit(-1);}ptrs->stack = ret;ptrs->capacity = newCapacity;} }//入棧 void PushStack(Stack* ptrs, DATATYPE data) {assert(ptrs);checkSys(ptrs);ptrs->stack[ptrs->top++] = data; }//出棧 void PopStack(Stack* ptrs) {assert(ptrs);assert(!StackEmpty(ptrs));ptrs->top--; }//訪問棧頂數據 DATATYPE StackTop(Stack* ptrs) {assert(ptrs);assert(!StackEmpty(ptrs));return ptrs->stack[ptrs->top - 1]; }//判斷棧是否為空 bool StackEmpty(Stack* ptrs) {assert(ptrs);return ptrs->top == 0; }//數據個數 int StackSize(Stack* ptrs) {assert(ptrs);return ptrs->top; }//銷毀棧 void DestroyStack(Stack* ptrs) {assert(ptrs);free(ptrs->stack);ptrs->top = ptrs->capacity = 0;ptrs->stack = NULL; } //測試邏輯 #define _CRT_SECURE_NO_WARNINGS 1 #include "StackQueue.h"void test() {Stack sk;InintStack(&sk);PushStack(&sk, 1);PushStack(&sk, 2);printf("%d ", StackTop(&sk));PopStack(&sk);PushStack(&sk, 3);PushStack(&sk, 4);printf("%d ", StackTop(&sk));PopStack(&sk);PushStack(&sk, 5);PushStack(&sk, 6);//遍歷完棧就相當于清空棧了while (!StackEmpty(&sk)){printf("%d ", StackTop(&sk));PopStack(&sk);}DestroyStack(&sk); } int main() {test();return 0; }

2. 隊列

2.1 隊列的概念及結構

隊列:只允許在一端進行插入數據操作,在另一端進行刪除數據操作的特殊線性表隊列具有先進先出FIFO(First In First Out)的規則
入隊列進行插入操作的一端稱為隊尾
出隊列:進行刪除操作的一端稱為隊頭

2.2 隊列的實現

隊列也可以數組和鏈表的結構實現,使用鏈表的結構實現更優一些,因為如果使用數組的結構,出隊列在數組頭上出數據,就需要挪動后面的數據,效率會比較低。

代碼實現:

//函數聲明 #pragma once #include <stdio.h> #include <assert.h> #include <stdlib.h> #include <stdbool.h> //創建隊列 typedef int QDataType; typedef struct QueueNode {QDataType data;struct QueueNode* next; }QNode;//因為隊列只有頭刪尾插 //因此需要定義兩個指針 //一個指向隊頭,另一個指向隊尾 typedef struct Queue {QNode* head;QNode* tail;int size; }Queue;// 初始化隊列 void QueueInit(Queue* q);// 隊尾入隊列 void QueuePush(Queue* q, QDataType data);// 隊頭出隊列 void QueuePop(Queue* q);// 獲取隊列頭部元素 QDataType QueueFront(Queue* q);// 獲取隊列隊尾元素 QDataType QueueBack(Queue* q);// 獲取隊列中有效元素個數 int QueueSize(Queue* q);// 檢測隊列是否為空,如果為空返回非零結果,如果非空返回0 int QueueEmpty(Queue* q);// 銷毀隊列 void QueueDestroy(Queue* q); //函數實現 #define _CRT_SECURE_NO_WARNINGS 1 #include "Queue.h" // 初始化隊列 void QueueInit(Queue* q) {assert(q);q->head = q->tail = NULL;q->size = 0; }// 隊尾入隊列 void QueuePush(Queue* q, QDataType data) {assert(q);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (!newnode){perror("malloc fail");exit(-1);}newnode->data = data;newnode->next = NULL;//當頭指針為空需要特殊處理if (q->head == NULL){q->head = q->tail = newnode;}else{q->tail->next = newnode;q->tail = q->tail->next;}q->size++; }// 隊頭出隊列 void QueuePop(Queue* q) {assert(q);assert(!QueueEmpty(q));if (q->head->next == NULL){free(q->head);q->head = q->tail = NULL;q->size = 0;}else{QNode* del = q->head;q->head = q->head->next;free(del);del = NULL;q->size--;} }// 獲取隊列頭部元素 QDataType QueueFront(Queue* q) {assert(q);assert(!QueueEmpty(q));return q->head->data; }// 獲取隊列隊尾元素 QDataType QueueBack(Queue* q) {assert(q);assert(!QueueEmpty(q));return q->tail->data; }// 獲取隊列中有效元素個數 int QueueSize(Queue* q) {assert(q);return q->size; }// 檢測隊列是否為空,如果為空返回非零結果,如果非空返回0 int QueueEmpty(Queue* q) {assert(q);return q->head == NULL && q->tail == NULL; }// 銷毀隊列 void QueueDestroy(Queue* q) {assert(q);QNode* cur = q->head;while (cur){QNode* del = cur;cur = cur->next;free(del);del = NULL;}q->head = q->tail = NULL; } //主邏輯測試 void testQueue() {Queue qq;QueueInit(&qq); QueuePush(&qq, 1);QueuePush(&qq, 2);printf("%d ", QueueFront(&qq));QueuePop(&qq);QueuePush(&qq, 3);QueuePush(&qq, 4);printf("%d ", QueueFront(&qq));QueuePop(&qq);QueuePush(&qq, 5);while (!QueueEmpty(&qq)){printf("%d ", QueueFront(&qq));QueuePop(&qq);}QueueDestroy(&qq); } int main() {//testStack();testQueue();return 0; }

注意:一個入隊列順序對應一個出隊列順序,一個入棧順序對應多個出棧順序

3. 概念選擇題

  • 一個棧的初始 狀態為空。現將元素1、2、3、4、5、A、B、C、D、E依次入棧,然后再依次出棧,則元素出棧的順序是( )。
    A 12345ABCDE
    B EDCBA54321
    C ABCDE12345
    D 54321EDCBA

  • 若進棧序列為 1,2,3,4 ,進棧過程中可以出棧,則下列不可能的一個出棧序列是()
    A 1,4,3,2
    B 2,3,4,1
    C 3,1,4,2
    D 3,4,2,1

  • 現有一循環隊列,其隊頭指針為front,隊尾指針為rear;循環隊列長度為N。其隊內有效長度為?(假設多給一個空間,實際長度為N)
    A (rear - front + N) % N + 1
    B (rear - front + N) % N
    C (rear - front) % (N + 1)
    D (rear - front + N) % (N - 1)

  • 答案:1.B 2.C 3.B

    第一題很簡單,先進后出原則。
    第二題需要注意的是在入棧的過程中是可以直接出棧的,比如說入棧的順序為1,2,3,4,那么出棧的順序也是1,2,3,4這是因為可以入1出1,入2出2…,因此只需要往選項中代入就可以找出不可能的一個順序。
    一個入棧順序是可能有多個出棧順序。
    第三題實際長度為N,有效范圍為N-1,直接帶入即可求出當前的有效數據。

    4. 棧和隊列OJ題

    4.1 括號匹配問題

    來源:Leetcode。OJ鏈接

    給定一個只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判斷字符串是否有效。

    有效字符串需滿足:

    左括號必須用相同類型的右括號閉合。
    左括號必須以正確的順序閉合。


    解題思路:本題解法所需要的數據結構正是棧。

  • 當遍歷到左括號時入棧
  • 遍歷到右括號時,拿出棧頂的元素進行比較,如果對應的左括號不匹配返回0
  • //直接造輪子! //創建棧 //... //把上面實現的棧原封不動的拷貝下來 bool isValid(char * s){Stack sk;InintStack(&sk);for(int i=0; s[i]; ++i){if(s[i] == '(' || s[i] == '[' || s[i] == '{'){//左括號入棧PushStack(&sk, s[i]);} else{if(StackEmpty(&sk)){//如果第一個就是右括號,棧為空,說明不匹配DestroyStack(&sk);return 0;}DATATYPE ret = StackTop(&sk);if(s[i] == ')' && ret != '(' || s[i] == ']' && ret != '[' || s[i] == '}' && ret != '{'){DestroyStack(&sk);return 0;}//沒次彈出棧頂元素PopStack(&sk);}}//最后判斷棧里的元素是否為空//為空才為正確答案int flag = StackEmpty(&sk);DestroyStack(&sk);return flag; }

    4.2 用隊列實現棧

    來源:Leetcode。OJ鏈接

    題目描述:請你僅使用兩個隊列實現一個后入先出(LIFO)的棧,并支持普通棧的全部四種操作(push、top、pop 和 empty)。

    實現 MyStack 類:

    void push(int x) 將元素 x 壓入棧頂。
    int pop() 移除并返回棧頂元素。
    int top() 返回棧頂元素。
    boolean empty() 如果棧是空的,返回 true ;否則,返回 false 。

    思路:由于隊列的性質是先進先出,和棧先進后出相反,因此需要用到兩個隊列q1和q2,其中一個為push的隊列,另一個為pop的輔助隊列。
    具體地,當pop時,找到兩個中不為空的隊列,把不為空的前N-1個數據全部依次push到為空的輔助隊列,此時剩下的一個元素(隊尾的元素)即為棧頂元素,pop后此隊列為空。當下一次pop時,也是同樣的操作。

    //造輪子 //... //... //把上面實現的隊列原封不動的拷貝下來//創建兩個隊列 typedef struct {Queue q1;Queue q2; } MyStack; //初始化 MyStack* myStackCreate() {MyStack* obj = (MyStack*)malloc(sizeof(MyStack));QueueInit(&obj->q1);QueueInit(&obj->q2);return obj; } //push元素到空隊列 void myStackPush(MyStack* obj, int x) {if(!QueueEmpty(&obj->q1)){QueuePush(&obj->q1, x);}else{QueuePush(&obj->q2, x);} } //找到不為空的一個隊列 //把前N-1個數據全部push到空隊列中 //返回最后一個元素后pop int myStackPop(MyStack* obj) {Queue* empty = &obj->q1, *noempty = &obj->q2;if(!QueueEmpty(empty)){empty = &obj->q2;noempty = &obj->q1;}//非空隊列的N-1個元素導入到空隊列,剩下的一個就是棧頂元素while(QueueSize(noempty) > 1){QueuePush(empty, QueueFront(noempty));QueuePop(noempty);}int tmp = QueueFront(noempty);QueuePop(noempty);return tmp; } //返回不為空隊列的隊尾的元素,即棧頂元素 int myStackTop(MyStack* obj) {if(!QueueEmpty(&obj->q1)){return QueueBack(&obj->q1);}else{return QueueBack(&obj->q2);} } //兩個隊列都為空才是空 bool myStackEmpty(MyStack* obj) {return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2); } //釋放隊列 void myStackFree(MyStack* obj) {QueueDestroy(&obj->q1);QueueDestroy(&obj->q2);free(obj); }

    4.3 用棧實現隊列

    來源:Leetcode。OJ鏈接
    請你僅使用兩個棧實現先入先出隊列。隊列應當支持一般隊列支持的所有操作(push、pop、peek、empty):

    實現 MyQueue 類:

    void push(int x) 將元素 x 推到隊列的末尾
    int pop() 從隊列的開頭移除并返回元素
    int peek() 返回隊列開頭的元素
    boolean empty() 如果隊列為空,返回 true ;否則,返回 false


    解題思路:棧的規則是先進后出,如何利用兩個棧實現先進先出模擬隊列?
    不難發現,如果一個棧只入數據,另一個棧從入棧中依次取出棧頂的數據后再出棧,這種出棧的順序正好符合先進先出。
    具體地,定義一個入棧s1,出棧s2,當入數據全部入到s1中,當出數據時判斷s2是否為空,如果為空,開始從s1的棧頂取出數據,每次取出后,pop掉s1中棧頂的數據。
    全部取出放入s2后,再進行出棧操作,數據的出棧順序就變成了先進先出。
    s2的棧頂就可以理解為隊列的隊頭,此時s2棧頂的數據依次出棧(出隊),就模擬出了隊列的效果。

    //造輪子 //... //... //把上面實現的棧原封不動的拷貝下來//創建結構體 typedef struct {Stack s1;Stack s2; } MyQueue;//創建兩個棧 //s1是入棧 //s2是出棧 MyQueue* myQueueCreate() {MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));InintStack(&obj->s1);InintStack(&obj->s2);return obj; } //入棧的數據直接存放到s1中 void myQueuePush(MyQueue* obj, int x) {PushStack(&obj->s1, x); } //判斷入棧是否為空 //如果為空則把入棧的數據全部取出放入出棧中 //這樣才符合隊列的先進先出 void PushToPop(Stack* push, Stack* pop) {if(StackEmpty(pop)){while(!StackEmpty(push)){PushStack(pop, StackTop(push));PopStack(push);}} } //出隊并刪除隊頭元素 //(棧的規則是先進后出,把入棧的順序反過來就變成了先進先出,也就是隊列的規則) int myQueuePop(MyQueue* obj) {PushToPop(&obj->s1, &obj->s2);int top = StackTop(&obj->s2);PopStack(&obj->s2);return top; } //出隊 int myQueuePeek(MyQueue* obj) {PushToPop(&obj->s1, &obj->s2);return StackTop(&obj->s2); } //兩個棧都不為空 bool myQueueEmpty(MyQueue* obj) {return StackSize(&obj->s1) == 0 && StackSize(&obj->s2) == 0; } //釋放棧 void myQueueFree(MyQueue* obj) {free((&obj->s1)->stack);free((&obj->s2)->stack);free(obj); }

    4.4 設計循環隊列

    來源:Leetcode。OJ鏈接

    設計你的循環隊列實現。 循環隊列是一種線性數據結構,其操作表現基于 FIFO(先進先出)原則并且隊尾被連接在隊首之后以形成一個循環。它也被稱為“環形緩沖器”。

    循環隊列的一個好處是我們可以利用這個隊列之前用過的空間。在一個普通隊列里,一旦一個隊列滿了,我們就不能插入下一個元素,即使在隊列前面仍有空間。但是使用循環隊列,我們能使用這些空間去存儲新的值。

    你的實現應該支持如下操作:

    • MyCircularQueue(k): 構造器,設置隊列長度為 k 。
    • Front: 從隊首獲取元素。如果隊列為空,返回 -1 。
    • Rear: 獲取隊尾元素。如果隊列為空,返回 -1 。
    • enQueue(value): 向循環隊列插入一個元素。如果成功插入則返回真。
    • deQueue(): 從循環隊列中刪除一個元素。如果成功刪除則返回真。
    • isEmpty(): 檢查循環隊列是否為空。
    • isFull(): 檢查循環隊列是否已滿。

    環形隊列可以使用數組實現,也可以使用循環鏈表實現。

    環形隊列第一個比較棘手的問題是如何判空以及如何判滿,有兩種方法:
    1、添加一個size變量用來記錄數據個數
    2、額外增加一個空間,滿的時候永遠留一個位置。比如說有效數據個數為4,那么開空間時開辟5個數據的空間,在判斷時會比較方便。

    如上圖,到這里其實可以發現使用鏈表來實現的話會有一個不好的地方,不方便取出尾結點的數據,因為rear那地方并不是有效數據的位置,除非再定義一個指針,指向尾結點的前一個結點,但是實現起來就比較麻煩了。但是使用數組就比較方便訪問了,因為可以支持下標快速訪問,無需遍歷,rear-1就可以取出最后一個位置的數據,因此這題選擇數組來實現。

    如果選擇數組,還有一個小問題是判滿情況,判滿的表達式為rear+1 == front,如果下標rear+1越界了如何處理?

    可以使用取模運算來巧妙地解決這個問題,因為該數組的實際大小為5(下標訪問范圍為0~4),而實際有效的范圍為4(下標訪問范圍為0~3),因此,當rear的下標為4的時候,說明到了數組的最后一個位置,此時令(rear+1) %= 5,讓其回到數組最開頭的位置,這也就是循環數組一個最基本的做法(到達最后一個位置時再回到最開始的位置),這時判斷rear == front。

    搞清楚這個之后,實現循環隊列就比較簡單了.

    typedef struct {int* arr;int front;int rear;int arrSize; } MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));//多增加一個位置obj->arr = (int*)malloc(sizeof(int)*(k+1));obj->front = obj->rear = 0;obj->arrSize = k+1;return obj; } //頭尾相等則為空 bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front == obj->rear; } //尾+1等于頭則為滿 bool myCircularQueueIsFull(MyCircularQueue* obj) {return ((obj->rear+1) % obj->arrSize) == obj->front; } //插入數據 bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if(myCircularQueueIsFull(obj)){return 0;}obj->arr[obj->rear] = value;obj->rear++;//++后如果==arrSize,讓其%arrSize回到0//小于%后值不變obj->rear %= obj->arrSize; return 1; } //刪除數據 bool myCircularQueueDeQueue(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){return 0;}//同樣的道理,如果++等于arrSizeobj->front++;obj->front %= obj->arrSize; return 1; } //返回隊頭數據 int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;return obj->arr[obj->front]; } //返回隊尾數據 int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;return obj->arr[(obj->rear-1) % obj->N]; }void myCircularQueueFree(MyCircularQueue* obj) {free(obj->arr);free(obj); }

    有點難哦

    總結

    以上是生活随笔為你收集整理的我幼儿园的弟看了都直呼简单的【栈和队列】的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。