文章目录
前言
本篇文章将为大家介绍一下栈和队列
一、栈
1.1栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
1.2栈的实现
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的 代价比较小。
栈的结构定义
typedef int STDataType;
typedef struct Stack
{
STDataType* a; //这里采用数组实现
int top; // 栈顶
int capacity; // 容量
}Stack;
栈的功能设想
// 初始化栈
void StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
初始化栈函数实现
void StackInit(Stack* ps)
{
assert(ps);//是否断言取决于外面是否定义了结构体
ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
if (ps->a == NULL)
{
perror("malloc fail");
exit(-1);
}
ps->top = 0;
ps->capacity = 4;
}
销毁栈函数实现
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
入栈函数实现
在进行插入函数之前我们必须先明白一点,我们表示栈顶的top初始化为-1,那么我们在进行入栈操作之后就应该+1.那么它所在的位置实则表示栈顶元素的下一个位置
void StackPush(Stack* ps, STDataType data)
{
assert(ps);
if (ps->capacity == ps->top)
{
//在原有的基础上开辟为2倍
STDataType* tmp = (STDataType*)realloc(ps->a,ps->capacity * 2 * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity *= 2;
}
ps->a[ps->top] = data;
ps->top++;
}
有了初始化,销毁,插入,我们可以简单的测试一下
void TestStack3()
{
Stack st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
StackPush(&st, 5);
StackDestroy(&st);
}
int main()
{
TestStack3();
return 0;
}
可以看见当我们在插入第五个元素的时候有一个开辟空间的操作
出栈函数实现
void StackPop(Stack* ps)
{
assert(ps);
//如果只有上面这一个断言,当栈里面为空的时候就会报错
assert(ps->capacity > 0);
ps->top--;
}
获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(ps->capacity > 0);
return ps->a[ps->top - 1];
}
获取栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
判空函数实现
int StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
二、队列
2.1队列的概念及结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
2.2队列的实现
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数 组头上出数据,效率会比较低。
队列结构定义
/ 链式结构:表示队列
typedef int QDataType;
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* front;
QNode* rear;
}Queue;
队列功能构造
// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
队列初始化函数实现
void QueueInit(Queue* q)
{
assert(q);
q->front = NULL;
q->rear = NULL;
}
队列销毁函数实现
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;
}
入队列函数实现
void QueuePush(Queue* q, QDataType data)
{
assert(q);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = data;
newnode->next = NULL;
if (q->rear == NULL)
{
q->front = q->rear = newnode;
}
else
{
q->rear->next = newnode;
q->rear = newnode;
}
}
出队列函数实现
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));//用判空函数来简化代码
if (q->front->next == NULL)//如果队头的下一个元素为空则说明队列只有一个元素
{
free(q->front);
q->front = q->rear = NULL;
}
else
{
QNode* del = q->front;
q->front = q->front->next;
free(del);
}
}
获取队列头部元素
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);
int size = 0;//如果想要优化可以在结构构造中加上size
QNode* cur = q->front;
while (cur)
{
cur = cur->next;
size++;
}
return size;
}
三、用队列实现栈
在开始写代码之前,我们首先要明白,栈是先进后出,队列是先进先出,那么我们可以两个队列
当我们入队列1234时,要出队列时出的是1,但是我们需要出的是4,这里我们就可以选择把数据倒到另一个列表里面,然后这个数据留下4,那么出的就可以是4了,那么我们要保证的就是一个队列保存数据,另一个为空,明确这一点后,我们开始实现
想要挑战一下自己的朋友们可以点击下方链接跳转leetcode解题
https://leetcode-cn.com/problems/implement-stack-using-queues/
结构构造
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* emptyQ=&obj->q1;
Queue* noneemptyQ=&obj->q2;
if(!QueueEmpty(&obj->q1))//判空,假设出错则交换
{
emptyQ=&obj->q2;
noneemptyQ=&obj->q1;
}
while(QueueSize(noneemptyQ)>1)//保留一个数据及要出的那个数
{
QueuePush(emptyQ,QueueFront(noneemptyQ));
QueuePop(noneemptyQ);
}
int top=QueueFront(noneemptyQ);
QueuePop(noneemptyQ);//取出来之后出掉,保证一个为空一个保存数据
return top;
}
取栈顶元素
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);
}
四,用栈实现队列
道理同用队列实现栈差不多,用两个队列来实现一个栈,队列先进先出,栈后进先出
当Push1234我们要出就是出4321,但是我们要的是1,那么我们就可以先把数据倒过来,这样数据就被颠倒了,那么这个时候出的就是1了,下次想要插入时我们可以选择再倒过来,但是我们还可以不倒,直接出,然后再另一个栈里面入,这样我们就可以定义为一个为入栈,一个为出栈,来形成一个队列
题目链接:https://leetcode-cn.com/problems/implement-queue-using-stacks/
这里就不给大家提供参考代码了,仅提供解题思路
总结
本篇文件对栈和队列进行了简单的讲解,并对其进行实现,以及进阶的用栈实现队列,用队列实现栈提供了思路,本篇文章到此就结束了,希望本篇文章对大家有所帮助,谢谢大家的支持。