做一款網(wǎng)站seoheuni
文章目錄
- 棧和隊列的回顧💻
- 棧🩳
- 隊列👟
- 棧和隊列經(jīng)典筆試題🔋
- 有效的括號🎸
- 用隊列實現(xiàn)棧 🕯
- 用棧實現(xiàn)隊列🔭
- 設(shè)計循環(huán)隊列🧼
安靜的夜晚 你在想誰嗎
棧和隊列的回顧💻
棧🩳
棧是一種特殊的線性表,其只允許在固定的一端進(jìn)行插入和刪除元素操作。進(jìn)行數(shù)據(jù)插入和刪除操作的一端稱為棧頂,另一端稱為棧底。棧中的數(shù)據(jù)元素遵守后進(jìn)先出LIFO(Last In First Out)的原則。
一般使用數(shù)組
實現(xiàn)棧
物理圖表示入棧和出棧(后進(jìn)先出)
隊列👟
隊列:只允許在一端進(jìn)行插入數(shù)據(jù)操作,在另一端進(jìn)行刪除數(shù)據(jù)操作的特殊線性表,隊列具有先進(jìn)先出。FIFO(First In First Out)
入隊列:進(jìn)行插入操作的一端稱為隊尾 ;出隊列:進(jìn)行刪除操作的一端稱為隊頭。
物理圖表示入隊和出隊(先進(jìn)先出)
棧和隊列經(jīng)典筆試題🔋
有效的括號🎸
力扣題目鏈接:有效的括號
給定一個只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判斷字符串是否有效。
有效字符串需滿足:
- 左括號必須用相同類型的右括號閉合。
- 左括號必須以正確的順序閉合。
- 每個右括號都有一個對應(yīng)的相同類型的左括號。
下面是幾個示例:
題目分析
本題通俗的來講,就是判斷括號是否是匹配的,即括號的類型是否匹配和數(shù)量是否匹配。我們可以使用棧的知識來解決這道題:從給定序列的第一個字符開始遍歷,如果遍歷遇到左括號,就入棧;如果遍歷遇到右括號,則先取棧頂元素,再出棧(因為合適的匹配必須是棧),判斷棧頂元素與這個右括號是否匹配。
需要注意的點有:盡量每次循環(huán)只遍歷一個元素或只對一個元素進(jìn)行判斷,這樣可以保證數(shù)量匹配的正確性。當(dāng)遍歷一個元素不是左括號的時候,就判斷棧中是否為空,如果棧為空,則說明數(shù)量是不匹配的;如果棧不為空,則要對這個右括號是否和棧頂?shù)淖罄ㄌ柶ヅ溥M(jìn)行判斷。
其實本題比較復(fù)雜的還是結(jié)構(gòu)的問題,畢竟不用C++的,這個棧的功能需要我們自己去實現(xiàn)。
力扣代碼(含棧結(jié)構(gòu))
typedef char STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;void STInit(ST* ps);
void STDestroy(ST* ps);
void STPush(ST* ps, STDataType x);
void STPop(ST* ps);
STDataType STtop(ST* ps);
int STsize(ST* ps);
bool STEmpty(ST* ps);
void STInit(ST* ps)
{ps->a = NULL;ps->top = 0;ps->capacity = 0;
}
void STDestroy(ST* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = 0;ps->top = 0;
}
void STPush(ST* ps, STDataType x)
{assert(ps);if (ps->top == ps->capacity){int NEWcapacity = (ps->capacity == 0) ? 4 : (ps->capacity * 2);//擴容STDataType* tmp = realloc(ps->a, sizeof(STDataType) * NEWcapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}ps->a = tmp;ps->capacity = NEWcapacity;}ps->a[ps->top] = x;ps->top++;
}
void STPop(ST* ps)
{assert(ps);assert(ps->top > 0);ps->top--;
}
//獲取棧頂元素
STDataType STtop(ST* ps)
{assert(ps);assert(ps->top>0);return ps->a[ps->top-1];
}int STsize(ST* ps)
{assert(ps);return ps->top;
}bool STEmpty(ST* ps)
{assert(ps);return (ps->top == 0);
}bool isValid(char * s){ST st;STInit(&st);char stack_top;while(*s){if(*s=='('||*s=='['||*s=='{'){STPush(&st,*s);} else{if(STEmpty(&st)){STDestroy(&st);return false;}stack_top=STtop(&st);STPop(&st);if(*s==')'&&stack_top!='('||*s==']'&&stack_top!='['||*s=='}'&&stack_top!='{'){STDestroy(&st);return false;}}s++;}if(!STEmpty(&st)){STDestroy(&st);return false;}return true;
}
當(dāng)代碼在所有不滿足的情況下依舊沒有返回 false 的時候,則說明它是滿足括號的有效性的。
用隊列實現(xiàn)棧 🕯
力扣題目鏈接:用隊列實現(xiàn)棧
你只能使用隊列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 這些操作。
你所使用的語言也許不支持隊列。 你可以使用 list (列表)或者 deque(雙端隊列)來模擬一個隊列 , 只要是標(biāo)準(zhǔn)的隊列操作即可。
范例:
題目分析及思路
隊列是先進(jìn)先出,要實現(xiàn)一個后進(jìn)先出的棧,一個隊列肯定是不行的,必須使用兩個隊列來互相導(dǎo)著來實現(xiàn)。例如,我現(xiàn)在要對這個數(shù)據(jù)結(jié)構(gòu)入4個元素:1 2 3 4
隊列只能 pop 先 push 的元素,而要達(dá)到將最后進(jìn)入的元素 pop 的目的,就需要另一個隊列來幫忙了:先將所有元素都push到隊列1,取 隊列1 頭位置的元素,將它 push 到 隊列2 中后,再將 隊列1 中這個元素 pop 掉。如此往復(fù),直到 隊列1 中只剩下一個元素,這就是棧結(jié)構(gòu)中需要 pop 的元素。
再將最后這個元素pop掉,就相當(dāng)于將棧結(jié)構(gòu)里的棧頂元素pop掉了。這樣就實現(xiàn)了棧的pop功能。
在上面的例子中,我們可以總結(jié)出隊列實現(xiàn)棧的一般規(guī)律:實現(xiàn)push數(shù)據(jù),就往空的隊列里push;實現(xiàn)pop數(shù)據(jù),先將費控隊列的前n-1個元素導(dǎo)入空隊列,并pop這n-1個元素,最后將剩下的那個元素pop掉即可實現(xiàn)棧的pop功能。
力扣代碼(含結(jié)構(gòu))
typedef int QDataType;
typedef struct QueueNode {QDataType data;struct QueueNode* next;
}QNode;
typedef struct Queue {QNode* head;QNode* tail;int size;
}Que;
void QueueInit(Que*pq);
void QueueDestroy(Que* pq);
void QueuePush(Que* pq, QDataType x);
void QueuePop(Que* pq);
QDataType QueueFront(Que* pq);
QDataType QueueBack(Que* pq);
bool QueueEmpty(Que* pq);
int QueueSize(Que* pq);
void QueueInit(Que* pq)
{assert(pq);pq->head = pq->tail = NULL;pq->size = 0;
}
void QueueDestroy(Que* pq)
{assert(pq);QNode* cur = pq->head;while (cur){QNode* next = cur->next;free(cur);cur = next;}
}
void QueuePush(Que* pq, QDataType x)
{assert(pq);QNode* nownode = (QNode*)malloc(sizeof(QNode));if (nownode == NULL){perror("malloc fail");exit(-1);}nownode->data = x;nownode->next = NULL;if (pq->tail == NULL){pq->head = pq->tail = nownode;}else{pq->tail->next = nownode;pq->tail = nownode;}pq->size++;
}
void QueuePop(Que* pq)
{assert(pq);assert(pq->head != NULL);if (pq->head->next == NULL){free(pq->head);pq->head = pq->tail = NULL;}else{QNode* next = pq->head->next;free(pq->head);pq->head = next;}pq->size--;
}
QDataType QueueFront(Que* pq)
{assert(pq);assert(pq->head != NULL);return pq->head->data;
}
QDataType QueueBack(Que* pq)
{assert(pq);assert(pq->head!=NULL);return pq->tail->data;}
bool QueueEmpty(Que* pq)
{assert(pq);return pq->head == NULL;
}
int QueueSize(Que* pq)
{assert(pq);return pq->size;
}typedef struct {Que q1,q2;
} MyStack;MyStack* myStackCreate() {MyStack*pst=(MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);//->的優(yōu)先級高于&,其實是&(pst->q1),將定義的結(jié)構(gòu)體變量的地址傳過去QueueInit(&pst->q2);return pst;
}void myStackPush(MyStack* obj, int x) {if(!QueueEmpty(&obj->q1)){QueuePush(&obj->q1,x);}else{QueuePush(&obj->q2,x);}
}int myStackPop(MyStack* obj) {Que*empty=&obj->q1;Que*nonEmpty=&obj->q2;if(!QueueEmpty(&obj->q1)){empty=&obj->q2;nonEmpty=&obj->q1;}//把非空隊列的前size-1個元素push到空隊列while(QueueSize(nonEmpty)>1){QueuePush(empty,QueueFront(nonEmpty));QueuePop(nonEmpty);}int top=QueueFront(nonEmpty);QueuePop(nonEmpty);return top;
}int myStackTop(MyStack* obj) {if(QueueEmpty(&obj->q1)){return QueueBack(&obj->q2);}else{return QueueBack(&obj->q1);}
}bool myStackEmpty(MyStack* obj) {return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);}void myStackFree(MyStack* obj) {QueueDestroy(&obj->q1);QueueDestroy(&obj->q2);free(obj);
}/*** Your MyStack struct will be instantiated and called as such:* MyStack* obj = myStackCreate();* myStackPush(obj, x);* int param_2 = myStackPop(obj);* int param_3 = myStackTop(obj);* bool param_4 = myStackEmpty(obj);* myStackFree(obj);
*/
用棧實現(xiàn)隊列🔭
力扣題目鏈接:用棧實現(xiàn)隊列
你 只能 使用標(biāo)準(zhǔn)的棧操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的語言也許不支持棧。你可以使用 list 或者 deque(雙端隊列)來模擬一個棧,只要是標(biāo)準(zhǔn)的棧操作即可。
范例:
題目分析及思路
題目要求只使用棧的基本操作實現(xiàn)一個隊列,這就需要兩個棧進(jìn)行‘合作’來完成。
例如,現(xiàn)在要往隊列中 push 四個數(shù)據(jù)1,2,3,4。
創(chuàng)建兩個棧,一個pushst,一個popst,如圖,先將這四個數(shù)據(jù)push到 pushst 這個棧中。
現(xiàn)在,如果要實現(xiàn)隊列的 pop 操作,就要將數(shù)據(jù)1 刪除,但棧只能pop棧頂元素,所以只能先將pushst中的‘上面’的三個數(shù)據(jù)先導(dǎo)過來(取棧頂元素,再pop),然后數(shù)據(jù)1 就變成了 pushst 的棧頂元素,直接pop即可。
接下來,如果隊列還需要 pop 數(shù)據(jù)的話,只需要在 popst 中 pop 即可。
如果要 push 數(shù)據(jù),直接push 到pushst中,再次push后,如果要pop數(shù)據(jù),需要將popst中的數(shù)據(jù)pop完后(直接取棧頂元素),將 pushst 中新push 的 n-1 個數(shù)據(jù)先導(dǎo)過去,再用上面的方式(出popst中的數(shù)據(jù)即可)。
總結(jié)
:定義兩個棧,隊列需要push數(shù)據(jù)的時候,先往pushst中push數(shù)據(jù)(此時棧popst中為空),首次需要pop數(shù)據(jù)的時候,先將pushst中push的n-1個數(shù)據(jù)導(dǎo)入popst中,然后將最后一個元素pop掉,這就是隊列要pop的頭。當(dāng)將n-1個數(shù)據(jù)導(dǎo)入popst中后,如果隊列再要pop數(shù)據(jù),就直接使用棧 popst 進(jìn)行pop數(shù)據(jù),入數(shù)據(jù)的時候就繼續(xù)在pushst中壓棧,當(dāng)popst中的數(shù)據(jù)pop完了直接還要pop的話,就需要再將pushst中的n-1個元素導(dǎo)過去,如此往復(fù)…
力扣代碼(含結(jié)構(gòu))
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;//棧頂位置int capacity;//??臻g大小
}ST;
void STInit(ST* ps);
void STPush(ST* ps,STDataType x);
void STPrint(ST* ps);
void STPop(ST* ps);
void STDestroy(ST* ps);
STDataType STTop(ST* ps);
int STSize(ST* ps);
bool STEmpty(ST* ps);void STInit(ST* ps)
{assert(ps);ps->a = NULL;ps->capacity = 0;ps->top = 0;
}
void STPush(ST* ps, STDataType x)
{assert(ps);if (ps->capacity==ps->top){int newcapacity = (ps->capacity == 0) ? 4 : ps->capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(ps->a));if (tmp == NULL){perror("realloc fail");exit(-1);}ps->capacity = newcapacity;ps->a = tmp;}ps->a[ps->top] = x;ps->top++;
}
void STPrint(ST* ps)
{assert(ps);int i = 0;for (i = 0; i < ps->top; i++){printf("%d ", ps->a[i]);}printf("\n");
}
void STPop(ST* ps)
{assert(ps);assert(ps->top>0);(ps->top)--;
}
void STDestroy(ST* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->top = 0;
}
STDataType STTop(ST* ps)
{assert(ps);assert(ps->top > 0);return ps->a[ps->top - 1];
}
int STSize(ST* ps)
{assert(ps);return ps->top;
}
bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}typedef struct {ST pushst;ST popst;
} MyQueue;MyQueue* myQueueCreate() {MyQueue*obj=(MyQueue*)malloc(sizeof(MyQueue));STInit(&obj->pushst);STInit(&obj->popst);return obj;
}void myQueuePush(MyQueue* obj, int x) {STPush(&obj->pushst,x);
}int myQueuePop(MyQueue* obj) {int front=myQueuePeek(obj);STPop(&obj->popst);return front;
}int myQueuePeek(MyQueue* obj) {if(STEmpty(&obj->popst)){while(STSize(&obj->pushst)>0){STPush(&obj->popst,STTop(&obj->pushst));STPop(&obj->pushst);}}return STTop(&obj->popst);
}bool myQueueEmpty(MyQueue* obj) {return STEmpty(&obj->popst)&&STEmpty(&obj->pushst);
}void myQueueFree(MyQueue* obj) {STDestroy(&obj->pushst);STDestroy(&obj->popst);free(obj);
}
設(shè)計循環(huán)隊列🧼
力扣題目鏈接:設(shè)計循環(huán)隊列
范例:
個人理解:當(dāng)我們使用數(shù)組(順序表)來實現(xiàn)隊列的時候,隨著出數(shù)據(jù)的時候隊頭不斷前移,那么隊列的容量(隊頭到隊尾)將會越來越小,如下圖:
所以可以采用循環(huán)隊列的方式來維持隊列容量的恒定。
此題需要的空間固定為k,并且要將這些空間重復(fù)利用,所以采用用數(shù)組實現(xiàn)最為合適。
思路
開辟數(shù)組空間的時候‘多開一個’,利用數(shù)組的下標(biāo)來控制隊尾和隊頭的位置。
比如,當(dāng)隊列的長度為4的時候,就開辟5塊空間的地址,最后一塊空間用來把握隊列長度來防止越界。當(dāng)隊頭和隊尾相等的時候,說明隊列為空;當(dāng)(隊尾+1)%(k+1)等于隊頭的時候,說明隊頭和隊尾之間只有一塊空間的地址,說明隊列已滿。
力扣代碼
typedef struct {int*a;int k;int front;int rear;
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));obj->a=(int*)malloc(sizeof(int)*(k+1));obj->k=k;obj->front=obj->rear=0;return obj;
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front==obj->rear;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->rear+1)%(obj->k+1)==obj->front;
}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if(myCircularQueueIsFull(obj))return false;obj->a[obj->rear]=value;obj->rear++;obj->rear%=obj->k+1;return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return false;obj->front++;obj->front%=obj->k+1;return true;
}int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;return obj->a[obj->front];
}int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;return obj->a[(obj->rear+obj->k)%(obj->k+1)];
}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);obj->a=NULL;free(obj);obj=NULL;
}/*** Your MyCircularQueue struct will be instantiated and called as such:* MyCircularQueue* obj = myCircularQueueCreate(k);* bool param_1 = myCircularQueueEnQueue(obj, value);* bool param_2 = myCircularQueueDeQueue(obj);* int param_3 = myCircularQueueFront(obj);* int param_4 = myCircularQueueRear(obj);* bool param_5 = myCircularQueueIsEmpty(obj);* bool param_6 = myCircularQueueIsFull(obj);* myCircularQueueFree(obj);
*/