目录
前情回顾:
前言:
题目一:《有效括号》
思路:
总结:
题目二:《用队列实现栈》
思路:
总结:
题目三:《用栈实现队列》
思路:
总结 :
总结:
前情回顾:
我们在上一篇好题分析中,分析了以下几题:
《反转链表》《相交链表》《环形链表(一)》《环形链表(二)》《随机链表的复制》《合并两个有序链表》
《链表中倒数第K个节点》《链表分割》《链表的回文结构》
上一篇的好题分析的blog:
好题分享(2023.11.5——2023.11.11)-CSDN博客
前言:
本次《好题分享》当中,我们主要熟悉我们刚刚所学的《栈和队列》这一部分内容。
通过三道Leecode题目使我们对于《栈和队列》能有一个更好的认识。
以下是我们需要解决的三道题目:
《有效括号》《用队列实现栈》《用栈实现队列》
这三道题的代码量都会非常的长,那是由于我们现在处于初学阶段,没有办法使用一些高效的方法来解决这些题目,但是不用怕,因为我们不仅仅实现过《栈》也实现过《队列》。
对于《栈和队列》的讲解可以去访问我的上一篇blog
《栈和队列》的模拟实现(顺序栈) (链队列)-CSDN博客
里面有需要实现本次题目的代码,打开我的Gitee即可获取。
在做题目之前我们首先需要将我们实现好的《栈》或者《队列》的代码粘贴到题目最前面的位置。
那么现在我们就来动手实践以及讲解
题目一:《有效括号》
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
typedef char STDataType;typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;void STInit(ST* pst)
{pst->a = NULL;pst->capacity = 0;pst->top = -1;
}void STDestory(ST* pst)
{assert(pst);free(pst);pst = NULL;
}void STPush(ST* pst, STDataType x)
{assert(pst);if (pst->capacity == pst->top || pst->top == -1){int newCapacity = (pst->capacity == 0) ? 4 : 2 * (pst->capacity);STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(ST)*newCapacity);if (tmp == NULL){perror("STPush -> realloc");exit(-1);}pst->a = tmp;pst->capacity = newCapacity;}pst->top++;pst->a[pst->top] = x;
}void STPop(ST* pst)
{assert(pst);pst->top--;
}STDataType STTop(ST* pst)
{assert(pst);return pst->a[pst->top];
}bool STEmpty(ST* pst)
{assert(pst);if (pst->top == -1){return true;}return false;
}int STSize(ST* pst)
{assert(pst);return (pst->top) + 1;
}void STPrint(ST* pst)
{assert(pst);while (pst->top != -1){printf("%d\n", STTop(pst));STPop(pst);}
}bool isValid(char* s) {ST st;STInit(&st);while(*s){if(*s == '[' || *s == '(' || *s == '{'){STPush(&st,*s);}else{if(STEmpty(&st)){return false;}else{char top = STTop(&st);STPop(&st);if((top == '(' && *s != ')')||(top == '{' && *s != '}')|| (top == '[' && *s != ']')){return false;}}}s++;}if(!STEmpty(&st)){return false;}return true;}
思路:
本题的思路就是利用《栈》的性质——先进后出
因为是匹配括号,我们不妨假设其本质就是一个《栈》。
我们先去遍历字符串,如果访问到左括号,例如
"{" "(" "["
就直接入栈。
而如果访问到右括号,我们就与栈顶的括号匹配,如果左右括号匹配成功就删除栈顶的括号,字符串继续遍历,直到遍历完成。
如图:
栈顶的括号与s指向的括号相匹配
此时相匹配,则删除栈顶元素:
如此继续遍历。
此时s指向'\0'代表遍历结束,而如果《栈》为空,就代表着字符串为有效括号,就可以return true
总结:
1.创建《栈》
2.遍历字符串,左括号入《栈》
3.遍历到右括号,则于《栈》顶的左括号相匹配
4.当字符串遍历完后,《栈》为空则return true;
但是这里存在一个细节,需要去注意
例如,如果我们在遍历字符串时遍历到有括号时,可是《栈》中没有栈顶元素与之匹配,则直接返回false。
题目二:《用队列实现栈》
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
typedef int QDataType;typedef struct QueueNode
{QDataType val;struct QueueNode* next;
}QNode;typedef struct Queue
{QNode* phead;QNode* ptail;int sz;
}Queue;void QueueInit(Queue* pq)
{pq->sz = 0;pq->phead = pq->ptail = NULL;
}void QueueDestroy(Queue* pq)
{assert(pq);assert(pq->phead);QNode* cur = pq->phead->next;while (cur){free(pq->phead);pq->phead = cur;cur = cur->next;}free(pq->phead);pq->phead = pq->ptail = cur = NULL;}void QueuePop(Queue* pq)
{assert(pq);if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else{QNode* tmp = pq->phead->next;free(pq->phead);pq->phead = tmp;}pq->sz--;
}void QueuePush(Queue* pq, QDataType x)
{assert(pq);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("QueuePush -> malloc");exit(-1);}newnode->val = x;newnode->next = NULL;if (pq->ptail == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = newnode;}pq->sz++;
}QDataType QueueFront(Queue* pq)
{assert(pq);return pq->phead->val;
}QDataType QueueBack(Queue* pq)
{assert(pq);return pq->ptail->val;
}bool QueueEmpty(Queue* pq)
{assert(pq);if (pq->phead == NULL){return true;}return false;
}int QueueSize(Queue* pq)
{return pq->sz;
}void QueuePrint(Queue* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){printf("%d->", cur->val);cur = cur->next;}printf("NULL\n");
}typedef struct {Queue q1;Queue q2;
} MyStack;MyStack* myStackCreate() {MyStack* pst = (MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);QueueInit(&pst->q2);return pst;
}void myStackPush(MyStack* obj, int x) {if (QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2)){QueuePush(&obj->q1, x);}else{if (!QueueEmpty(&obj->q1)){QueuePush(&obj->q1, x);}else{QueuePush(&obj->q2, x);}}
}int myStackPop(MyStack* obj) {if (QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2)){return 0;}else{if (!QueueEmpty(&obj->q1)){int n = QueueSize(&obj->q1) - 1;while (n){int top = QueueFront(&obj->q1);QueuePush(&obj->q2, top);QueuePop(&obj->q1);n--;}int top = QueueFront(&obj->q1);QueuePop(&obj->q1);return top;}else{int n = QueueSize(&obj->q2) - 1;while (n){int top = QueueFront(&obj->q2);QueuePush(&obj->q1, top);QueuePop(&obj->q2);n--;}int top = QueueFront(&obj->q2);QueuePop(&obj->q2);return top;}}
}int myStackTop(MyStack* obj) {if (QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2)){return 0;}else{if (!QueueEmpty(&obj->q1)){return QueueBack(&obj->q1);}else{return QueueBack(&obj->q2);}}
}bool myStackEmpty(MyStack* obj) {if (!QueueEmpty(&obj->q1) || !QueueEmpty(&obj->q2)){return false;}return true;}void myStackFree(MyStack* obj) {if (!QueueEmpty(&obj->q1) && !QueueEmpty(&obj->q2)){if (!QueueEmpty(&obj->q1)){QueueDestroy(&obj->q1);}else{QueueDestroy(&obj->q2);}}
}/*** 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);
*/
该题目的代码量会非常长,刚开始做题时是会产生恐惧,但是不用害怕,因只要我们了解了其中代码的秘密,我们就能迎刃而解了。
思路:
由题意,就是让我们创建两个栈,然后通过两个栈之间的操作,完成一个队列的基本实现。
我们想要插入12345,那么对于栈来说
1就是《栈》底元素,5就是《栈》顶元素。
出栈也只能出5
但是对于队列而言呢?
队头是1,队尾是5
出队列也只能出1.
所以对于“出栈”就可以利用两个队列:
先让12345插入到队列1,即q1
再让q1中的各个元素一个个的进到队列q2中
如此不断循环
直到q1只剩一个元素,那个元素就是对应的“栈顶”。
我们将这个元素删除即可:
最后则返回top:
以上就是“出栈”的函数实现,那么对于入栈来说,就是哪个队列不为空就插入到哪个队列中。
具体的代码还是好实现的。后续的函数我也不做过多的赘述。
总结:
对于这道题我们讲解关于“出栈”函数的实现就可以解决大部分问题。
但是这道题的自定义数据类型可能会有人绕不清楚,接下来我画一张图大家就可以理解了:
我们在上一篇blog也讲过,创建一个结构体用来保存头指针和尾指针对象,可以大大提高传输效率,也可以使人理解透彻!
题目三:《用栈实现队列》
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
typedef int STDataType;typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;void STInit(ST* pst)
{assert(pst);pst->a = NULL;pst->capacity = 0;pst->top = -1;
}void STDestory(ST* pst)
{assert(pst);free(pst->a);pst->a = NULL;pst->top = -1;pst->capacity = 0;
}void STPush(ST* pst, STDataType x)
{assert(pst);int newcapacity = (pst->capacity == 0) ? 4 : 2 * (pst->capacity);STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);if (tmp == NULL){perror("STPush -> realloc");exit(-1);}pst->a = tmp;pst->capacity = newcapacity;pst->top++;pst->a[pst->top] = x;}void STPop(ST* pst)
{assert(pst);if (pst->top != -1){pst->top--;}
}STDataType STTop(ST* pst)
{assert(pst);if (pst->top != -1){return pst->a[pst->top];}return -1;
}bool STEmpty(ST* pst)
{assert(pst);if (pst->top == -1){return true;}return false;
}int STSize(ST* pst)
{assert(pst);return pst->top + 1;
}void STPrint(ST* pst)
{assert(pst);for (int i = 0; i <= pst->top; i++){printf("%d ", pst->a[i]);}
}typedef struct {ST SPush;ST SPop;
} MyQueue;MyQueue* myQueueCreate() {MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));STInit(&obj->SPush);STInit(&obj->SPop);return obj;
}void myQueuePush(MyQueue* obj, int x) {STPush(&obj->SPush,x);
}int myQueuePop(MyQueue* obj) {int front = myQueuePeek(obj);STPop(&obj->SPop);return front;
}int myQueuePeek(MyQueue* obj) {if(STEmpty(&obj->SPop)){while(!STEmpty(&obj->SPush)){STPush(&obj->SPop,STTop(&obj->SPush));STPop(&obj->SPush);}}return STTop(&obj->SPop);
}bool myQueueEmpty(MyQueue* obj) {if(STEmpty(&obj->SPush) && STEmpty(&obj->SPop)){return true;}return false;
}void myQueueFree(MyQueue* obj) {STDestory(&obj->SPop);STDestory(&obj->SPush);free(obj);
}/*** Your MyQueue struct will be instantiated and called as such:* MyQueue* obj = myQueueCreate();* myQueuePush(obj, x);* int param_2 = myQueuePop(obj);* int param_3 = myQueuePeek(obj);* bool param_4 = myQueueEmpty(obj);* myQueueFree(obj);
*/
本题的代码与上题一样,先将实现栈的函数粘贴到该题目中。
思路:
我们还是先来讲讲“出队列”这个思路。
“出队列”秉持一个原则,就是先进先出
而出缺是后进先出,所以我们不得不继续用到两个栈。
但是这里我们不同于上一道题,我们必须定义两个栈
即一个栈只能入数据,一个栈是用来出数据的。
比如我还是将12345入栈。
如果是队列的话,只能实现先进先出,即将1删除。
所以我们可以借鉴上题那样,先将SPush里面的所以元素移到SPop中 :
这样一来,我们只需要删除SPop的栈顶元素即可。
如果我们想要继续输入数据,也不需要将SPop里面的数据再倒入SPush中,只需要再SPush里面继续添加新数据。
反正,只要SPop这个栈里有数据,就先删除栈顶的,而对于添加数据,只需要向SPush里面添加即可。
当删完SPop后如果还想删数据,则继续将SPush里面的数据倒入SPop即可。
总结 :
当然还是会有人对数据类型不是很理解,我还是画个图给大家观看:
总结:
本文主要以巩固《栈和队列》为主,从此以后的好题分析,我们主要给大家讲解最关键思路,以及部分重难点函数的实现。
同学们下来可以自己动手尝试编写编写。
记住
“坐而言不如起而行!”
Aciton speak louder than words!