本期博客给大家带来了几道经典栈和队列题,吃透它简直易如反掌~
1.括号匹配问题
题目地址:20. 有效的括号 - 力扣(Leetcode)
解题思路:在这里我们创建一个栈,每次将字符入栈之前先对比栈顶元素是否相同,相同则连同栈顶元素一起出栈,不相同则将字符入栈。最后字符串全部以此操作完毕之后如果栈为空则为有效括号,否则则不满足题目条件。
解题代码:
bool isValid(char* s)
{
int num=0;
while(*(s+num))
{
num++;
}
if(num%2)
return false;
char *stack=(char*)malloc(sizeof(char)*num);
num=0;
while(*s)
{
if(*s=='('||*s=='['||*s=='{')
{
*(stack+num)=*s;
num++;
s++;
}
else if(*s==')')
{
if(num==0)
return false;
if(*(stack+num-1)!='(')
return false;
num--;
s++;
}
else if(*s==']')
{
if(num==0)
return false;
if(*(stack+num-1)!='[')
return false;
num--;
s++;
}
else if(*s=='}')
{
if(num==0)
return false;
if(*(stack+num-1)!='{')
return false;
num--;
s++;
}
}
if(num==0)
return true;
else
return false;
}
2. 用栈来实现队列
题目地址:225. 用队列实现栈 - 力扣(Leetcode)
如何用两个队列来实现一个栈?
解题思路:在这里入栈时,选择一个栈来存储数据(即进不为空的队列),都为空时我们随机选择一个空栈将要入栈的元素全部导入到这个队列中。在出栈时,我们直接将装有数据的队列除队头元素之外全部出队列到另一个空队列中,最后将最后一个队头元素出队列就可以了。
入栈思路模拟图:
随机选择一个队列进行入数据:
出栈思路模拟图:
解题代码:
在这里由于C语言没有为我们提供队列,我们这里要自己造“轮子”来创建一些队列的基本操作
//数据
typedef int QDataType;
//链表节点
typedef struct QListNode
{
struct QListNode* _next;
QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* _front;//队头
QNode* _rear;//队尾
int size;//记录节点总数
}Queue;
// 初始化队列
void QueueInit(Queue* q)
{
assert(q);//传入的指针不能为空
//将队头和队尾指针都初始化为空
q->_front = NULL;
q->_rear = NULL;
q->size = 0;//置0
}
// 检测队列是否为空
bool QueueEmpty(Queue* q)
{
assert(q);//传入的指针不能为空
return q->_front == NULL && q->_rear == NULL;//如果队头队尾指针都为空返回的就是真值
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
assert(q);//传入的指针不能为空
QNode* newnode = (QNode*)malloc(sizeof(QNode));//为新元素开辟空间
if (newnode == NULL)//判断是否开辟成功
{
perror("malloc");
exit(-1);
}
newnode->_data = data;
newnode->_next = NULL;
//注意这里尾插时要判断队中是否为空,是空的话需要改变队头和队尾指针的指向
if (QueueEmpty(q))
{
q->_front = q->_rear = newnode;
}
else
{
q->_rear->_next = newnode;
q->_rear = q->_rear->_next;
}
q->size++;//加1
}
// 队头出队列
void QueuePop(Queue* q)
{
assert(q);//传入的指针不能为空
assert(!QueueEmpty(q));//队列不能为空
QNode* del = q->_front;
//注意这里尾插时要判断队中是否为空,如果是的话需要将队头队尾指针置为空
if (q->_front == q->_rear)
{
q->_front = q->_rear = NULL;
}
else
{
q->_front = q->_front->_next;
}
free(del);//释放节点空间
q->size--;//减1
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
assert(q);//传入的指针不能为空
assert(!QueueEmpty(q));//队列不能为空
return q->_front->_data;//返回队列头部元素
}
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
assert(q);//传入的指针不能为空
assert(!QueueEmpty(q));//队列不能为空
return q->_rear->_data;//返回队列队尾元素
}
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
assert(q);//传入的指针不能为空
return q->size;//返回累计的个数
}
// 销毁队列
void QueueDestroy(Queue* q)
{
assert(q);//传入的指针不能为空
QNode* cur = q->_front;
//释放链表空间
while (cur)
{
QNode* del = cur;
cur = cur->_next;
free(del);
}
//将队头和队尾指针置空(防止野指针的产生)
q->_front = q->_rear = NULL;
q->size = 0;//置0
}
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
QueueInit(&obj->q1);
QueueInit(&obj->q2);
return obj;
}
void myStackPush(MyStack* obj, int x) {
if (!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1, x);
}
else {
QueuePush(&obj->q2, x);
}
}
int myStackPop(MyStack* obj) {
Queue* EmptyQueue=&obj->q1;
Queue* UnEmptyQueue=&obj->q2;
if(QueueEmpty(UnEmptyQueue))
{
EmptyQueue=&obj->q2;
UnEmptyQueue=&obj->q1;
}
while(QueueSize(UnEmptyQueue)>1)
{
QueuePush(EmptyQueue, QueueFront(UnEmptyQueue));
QueuePop(UnEmptyQueue);
}
int temp=QueueFront(UnEmptyQueue);
QueuePop(UnEmptyQueue);
return temp;
}
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);
}
3. 用栈实现队列
题目地址:232. 用栈实现队列 - 力扣(Leetcode)
我们既然可以用队列来实现一个栈,那必须得试试用栈来实现队列
解题思路:我们使用两个栈s1和s2来实现队列,s1作为专门入队列的栈,s2作为专门出队列的栈。在入数据时,只用s1这个栈来存储数据。在出队列时,如果s2为空则将s1中的数据全部出栈到s2后,再由s2出栈,如果s2不为空则直接从s2中直接出栈,这样就实现了数据的颠倒。
入队列思路模拟图:
出队列思路模拟图:
解题代码:
在这里由于C语言没有为我们提供栈,我们这里要自己造“轮子”来创建一些栈的基本操作
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
STDataType* _a;
int _top; // 栈顶
int _capacity; // 容量
}Stack;
// 初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->_a = (STDataType*)malloc(sizeof(STDataType)*4);
if (ps->_a == NULL)
{
perror("malloc");
exit(-1);
}
ps->_capacity = 4;
ps->_top = 0;
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{
assert(ps);//传入的指针不能为空
if (ps->_capacity == ps->_top)//判断栈是否已满,满了就扩容
{
STDataType* temp = (STDataType*)realloc(ps->_a, (ps->_capacity + 4) * sizeof(STDataType));
if (temp == NULL)
{
perror("realloc");
exit(-1);
}
ps->_a = temp;
ps->_capacity += 4;
}
ps->_a[ps->_top] = data;//向栈内添加数据
ps->_top++;//栈顶增加1
}
// 出栈
void StackPop(Stack* ps)
{
assert(ps);//传入的指针不能为空
if (ps->_top <= 0)//栈空就不可出栈了
return;
ps->_top--;
}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);//传入的指针不能为空
assert(ps->_top > 0);//栈不能为空
return ps->_a[ps->_top - 1];//返回栈顶元素
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool StackEmpty(Stack* ps)
{
assert(ps);//传入的指针不能为空
return ps->_top == 0;//判断栈是否为空
}
// 销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);//传入的指针不能为空
free(ps->_a);//释放栈数据空间
ps->_capacity = 0;//初始栈容量为4
ps->_top = 0;//初始栈内元素为0
}
typedef struct {
Stack s1;
Stack s2;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
StackInit(&obj->s1);
StackInit(&obj->s2);
return obj;
}
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->s1,x);
}
int myQueuePop(MyQueue* obj) {
if(StackEmpty(&obj->s2))
{
while(!StackEmpty(&obj->s1))
{
StackPush(&obj->s2,StackTop(&obj->s1));
StackPop(&obj->s1);
}
}
int tmp=StackTop(&obj->s2);
StackPop(&obj->s2);
return tmp;
}
int myQueuePeek(MyQueue* obj) {
if(StackEmpty(&obj->s2))
{
while(!StackEmpty(&obj->s1))
{
StackPush(&obj->s2,StackTop(&obj->s1));
StackPop(&obj->s1);
}
}
return StackTop(&obj->s2);
}
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->s1)&&StackEmpty(&obj->s2);
}
void myQueueFree(MyQueue* obj) {
StackDestroy(&obj->s1);
StackDestroy(&obj->s2);
free(obj);
}
4. 循环队列
题目地址:622. 设计循环队列 - 力扣(Leetcode)
解题思路:在这里我们使用数组来实现一个循环队列(用链表不方便查找节点位置,个方面没有数组有优势)。我们首先要确定队列的大小为实际所要存储元素总个数+1(即k+1),在实际要存储元素空间大小上加一是为了解决判空判满的问题。队列为空时front应该等于rear,为满时应有(rear+1)%(k+1)=front。
解题代码:
typedef struct {
int *a;//数组
int front;//队头
int rear;//队尾
int k;//空间大小
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a=(int *)malloc(sizeof(int)*(k+1));//多开一个空间解决判空判满的问题
obj->front=obj->rear=0;
obj->k=k;
return obj;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
assert(obj);
return (obj->rear+1)%(obj->k+1)==obj->front;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
assert(obj);
return obj->rear == obj->front;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->rear++]=value;
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);
free(obj);
}
上面就是本期博客的全部内容了,代码量较多,如有纰漏还请各位大佬不吝赐教!
我们下一期见~