栈和队列相关代码在前面已经实现过了,接下来就手撕几道有关栈和队列的几道高频经典OJ,让大家更加深刻的理解栈和队列这两种数据结构。
第一题:用栈实现队列
OJ链接:
题目要求用两个栈实现一个队列,要求具备队列基本接口:入列,出列,返回队列头元素,判断队列是否为空。接下来我们按照 ” 一定能写出来四步流程 “,来完成这个题目。
一.思路分析:
我们知道,队列有先进先出(FIFO)的原则,所以要想用两个栈实现队列,就必须用这两个栈使得数据先进先出。因为栈是先进后出的原则,会使得出数据顺序相反,所以用两个栈就刚好可以把顺序摆正。例如 1 2 3 4进入第一个栈---->4 3 2 1进入第二个栈---->1 2 3 4
二.细节分析
我们把两个栈分别取名为pushstack、popstack。即一个栈专门用来入队列,一个栈专门用来出队列。
1.当有元素要入队列:只需要把这个元素入栈到pushstack。
2.当有元素要出队列:如果popstack栈中有元素,直接出栈即可。如果popstack栈中没有元素,需要将pushstack栈中的元素捯入popstack栈。再出popstack栈。
3.返回队首元素:如果popstack栈中有元素,直接返回栈顶元素即可。如果popstack栈中没有元素,需要将pushstack栈中的元素捯入popstack栈。再返回popstack栈顶元素。
4.判断队列是否为空:若两个栈都为空,则队列为空。
三.代码实现
//栈的实现 typedef int DateType; typedef struct Stack//栈的总架构(这里是动态栈) { DateType* arr; int top;//栈顶下标 int capcity;//栈的容量 }Stack; //下面对栈的操作都是改变结构体内部的属性,参数列表传结构体一级指针 void IntiStack(Stack* stack);//初始化栈 void PushStack(Stack* stack, DateType n);//进栈,尾插 void PopStack(Stack* stack);//出栈,尾删 bool StackEmpty(Stack* stack);//检测栈是否为空 int StackSize(Stack* stack);//获取栈中有效数据个数 DateType TopStack(Stack* stack);//获取栈顶数据 void DestoryStack(Stack* stack);//销毁栈 void IntiStack(Stack* stack) { DateType* tmp = (DateType*)malloc(4*sizeof(DateType)); assert(tmp); stack->arr = tmp; stack->top = 0; stack->capcity = 4; } void PushStack(Stack* stack, DateType n) { assert(stack); if (stack->top == stack->capcity) { //扩容 DateType* tmp = realloc(stack->arr,2 * stack->capcity * sizeof(DateType)); assert(tmp); stack->arr = tmp; stack->capcity *= 2; } stack->arr[stack->top] = n; stack->top++; } void PopStack(Stack* stack) { assert(stack); assert(!StackEmpty(stack)); stack->top--; } bool StackEmpty(Stack* stack) { assert(stack); return stack->top == 0; } int StackSize(Stack* stack) { assert(stack); return stack->top; } DateType TopStack(Stack* stack) { assert(stack); assert(!StackEmpty(stack)); return stack->arr[stack->top - 1]; } void DestoryStack(Stack* stack) { assert(stack); free(stack->arr); stack->arr = NULL; stack->capcity = 0; stack->top = 0; } //开始解题 typedef struct//定义队列 { Stack pushstack;//定义两个栈 Stack popstack; } MyQueue; MyQueue* myQueueCreate()//初始化队列 { MyQueue* queue=(MyQueue*)malloc(sizeof(MyQueue));//为队列分配空间,顺便为两个栈分配空间 IntiStack(&queue->pushstack);//初始化两个栈 IntiStack(&queue->popstack); return queue; } void myQueuePush(MyQueue* obj, int x)//入列 { PushStack(&obj->pushstack,x);//把这个元素入栈到pushstack } int myQueuePop(MyQueue* obj)//出列 { int tmp=myQueuePeek(obj);//获取队列头元素(popstack栈顶元素) PopStack(&obj->popstack);//删除popstack栈顶元素 return tmp;//返回队列头元素 } int myQueuePeek(MyQueue* obj)//获取队首元素 { if(StackEmpty(&obj->popstack))//如果popstack栈为空 { //导pushstack栈的数据->popstack栈 while(!StackEmpty(&obj->pushstack))//循环条件:pushstack栈不为空 { //将pushstack栈顶元素入栈到popstack栈 PushStack(&obj->popstack,TopStack(&obj->pushstack)); //删除pushstack栈顶元素 PopStack(&obj->pushstack); } } return TopStack(&obj->popstack);//返回popstack栈顶元素 } bool myQueueEmpty(MyQueue* obj)//判断队列是否为空 { //两个栈都为空,队列为空,返回true return StackEmpty(&obj->pushstack)&&StackEmpty(&obj->popstack); } void myQueueFree(MyQueue* obj)//销毁队列 { DestoryStack(&obj->pushstack);//先销毁里面两个栈中的顺序表空间 DestoryStack(&obj->popstack); free(obj);//再释放队列空间(可以释放两个栈的空间,但是无法是释放栈里面的顺序表空间!) }
四.总结
用栈实现队列本质需要了解栈与队列的区别,知道二者数据处理的差异,并灵活应用。