一.让我们复习一下什么是“栈”和“队列”
1.栈是什么?
栈作为一种基本的数据结构,它的主要特点是后进先出,类似于一口大缸一样:
数据从上至下加入,取数据时只能由表面取出。我们可以通过顺序表的方式进行实现,也可以通过链表进行实现,由于栈不需要从栈底获取元素,所以减少顺序表头插麻烦的劣势,所以我使用顺序表实现。
typedef int STdata;
typedef struct stack {
STdata* a;
int top;
int capacity;
}ST;
void STInit(ST* pst);
void STPush(ST* pst, STdata x);
void STPop(ST* pst);
STdata STTop(ST* pst);
bool STEmpty(ST* pst);
int STSize(ST* pst);
void STDestroy(ST* pst);
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top=0;
pst->capacity = 0;
}
void STPush(ST* pst, STdata x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newcapacity =pst->capacity==0 ? 4 : 2*pst->capacity;
STdata* tmp = (STdata*)realloc(pst->a,sizeof(STdata)*newcapacity);
if (tmp==NULL)
{
perror("realloc fail");
}
pst->a = tmp;
pst->capacity = newcapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
void STPop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
pst->top--;
}
STdata STTop(ST* pst)
{
assert(pst);
// 不为空
assert(pst->top > 0);
return pst->a[pst->top - 1];
}
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top==0;
}
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->capacity = 0;
pst->top = 0;
}
我们来简单测试一下各项功能:
2.队列是什么?
正如其名一般,队列好像一个排着的对,先进先出:
使用者在队尾加入数据,从队头获得数据,中间数据不访问,恰好对应了单链表的优势,所以我们也来简单实现一下:
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>
typedef int Qdata;
typedef struct queuenode {
Qdata val;
struct queuenode* next;
}QNode;
typedef struct queue {
QNode* phead;
QNode* ptail;
int size;
}queue;
void QueueInit(queue* pq);
void QueuePush(queue* pq, Qdata x);
void QueuePop(queue* pq);
Qdata QueueFront(queue* pq);
Qdata QueueBack(queue* pq);
bool QueueEmpty(queue* pq);
int QueueSize(queue* pq);
void QueueDestroy(queue* pq);
void QueueInit(queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
void QueuePush(queue* pq, Qdata x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("newnode creat fail");
}
newnode->next = NULL;
newnode->val = x;
if (pq->ptail == NULL)
{
pq->ptail = pq->phead = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
void QueuePop(queue* pq)
{
assert(pq);
assert(pq->phead);
QNode* del = pq->phead;
pq->phead = pq->phead->next;
free(del);
if (pq->phead == NULL)
pq->ptail = NULL;
pq->size--;
}
Qdata QueueFront(queue* pq)
{
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
QueueBack(queue* pq)
{
assert(pq);
assert(pq->phead);
return pq->ptail->val;
}
bool QueueEmpty(queue* pq)
{
assert(pq);
return pq->phead == NULL;
}
int QueueSize(queue* pq)
{
assert(pq);
return pq->size;
}
void QueueDestroy(queue* pq)
{
assert(pq);
QNode* del = pq->phead;
while (del)
{
QNode* next = del->next;
free(del);
del = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
我们使用phead和ptail指针来维护,队列的头和尾,还可以加入一个size来动态记录队列成员数量。
二.思想进阶,队列和栈怎么互相转换呢?
1.用两个队列实现一个栈
题目链接:题目1
在做题之前,由于我们用的纯c语言,没有标准库,所以先自我实现一下队列。
做题思路是永远保持两个队列,一个空队列,一个存数据的队列,当队列push时,则直接添加到有数据队列的队尾,当需要删除时,则需要将当前队列转移至空队列只剩队尾一个,然后删除那一个元素。
1.先创建Mystack栈的结构体并进行初始化(直接malloc至堆上,函数使用完后空间并不会销毁)
typedef struct {
queue q1;
queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack*pst= (MyStack*)malloc(sizeof(MyStack));
QueueInit(&pst->q1);
QueueInit(&pst->q2);
return pst;
}
2.判空函数
bool myStackEmpty(MyStack* obj) {
if(QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2))
{
return true;
}
return false;
}
3.push
void myStackPush(MyStack* obj, int x) {
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
如果,队列不为空,则继续推入,反之推入另一个。
4.pop
int myStackPop(MyStack* obj) {
queue*Emptyq=&obj->q1;
queue*nonEmptyq=&obj->q2;
if(!QueueEmpty(&obj->q1))
{
Emptyq=&obj->q2;
nonEmptyq=&obj->q1;
}
while(QueueSize(nonEmptyq)>1)
{
QueuePush(Emptyq,QueueFront(nonEmptyq));
QueuePop(nonEmptyq);
}
int top=QueueFront(nonEmptyq);
QueuePop(nonEmptyq);
return top;
}
先假定q1为空进行创建指针,然后判断纠正。进行遍历,导入数据。
5.最后收尾即可
int myStackTop(MyStack* obj) {
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
}
2.用两个栈实现一个队列
题目:题目2
用栈实现队列,关键在于设置两个不同作用的栈,pop栈,push栈,每当需要pop时先检查是否pop栈为空,若为空则将push栈取出,放入pop栈,从栈底删除,相当于删了队列头数据。
1.创建MyQueue
typedef struct {
ST pushstack;
ST popstack;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
STInit(&obj->pushstack);
STInit(&obj->popstack);
return obj;
}
2.判空函数
bool myQueueEmpty(MyQueue* obj) {
return STEmpty(&obj->pushstack)&&STEmpty(&obj->popstack);
}
3.返回队头元素和删除函数
int myQueuePeek(MyQueue* obj) {
if(!STEmpty(&obj->popstack))
return STTop(&obj->popstack);
else
{
while(!STTop(&obj->pushstack))
{
STPush(&obj->popstack,STTop(&obj->pushstack));
STPop(&obj->pushstack);
}
return STTop(&obj->popstack);
}
}
int myQueuePop(MyQueue* obj) {
int top=myQueuePeek(obj);
STPop(&obj->popstack);
return top;
}
4.push
void myQueuePush(MyQueue* obj, int x) {
STPush(&obj->pushstack,x);
}
5.最后进行收尾
void myQueueFree(MyQueue* obj) {
STDestroy(&obj->pushstack);
STDestroy(&obj->popstack);
free(obj);
}
这两个题看似很无聊,实则对理解这两种数据结构意义重大。
本期就到这里,希望各位一键三联。