前言【必看】
学完栈和队列的宝子欢迎参观。。。【没有学过的宝子可能不太理解,因为注释少,实现多。【主要是懒(滚0
一、栈的实现
浅造一个栈吧
因为栈只在顶部进行操作,所以用顺序表的形式就OK,用链表的话从头遍历到尾【太累了,为什么要用动态数组的方式呢,因为我们空间不够就开辟,如果静态的话,那也太不方便了吧
1.以动态数组的方式创一个栈
typedef int STDataType;//用STDataType来表示int类型,主要是方便全局更换类型
typedef struct Stack
{
STDataType* a;//数组
int top; // 栈顶
int capacity; // 容量
}Stack;
2.栈的一系列操作函数
不要被吓到,其实还是很easy的
// 初始化栈
void StackInit(Stack* ps);
//判断栈满并扩容
void JugdeFull(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);
3.栈的相关函数的实现
1.初始化栈
所谓的初始化,就是给栈(结构体)中的每一个成员变量一个明确的值。初始化非常重要,创建一个栈的变量后,如果不初始化就开始一顿操作,那你就等着边啃泡面边改代码吧~
// 初始化栈
void StackInit(Stack* ps)
{
ps->a = NULL;
ps->capacity = ps->top = 0;
}
2.判断栈满
判断栈满这个函数不是栈基本操作的函数,但是由于许多函数调用时需要判断是否栈满,所以一般会单独封装一个函数,提高代码的复用性
//判断栈满并扩容
void JugdeFull(Stack* ps)
{
if (ps->top == ps->capacity)
{
//扩容:如果一开始容量就是0,那就给4的容量,如果一开始不是0容量,就扩为原来容量的2倍
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
//这里为什么要新创一个指针变量来接收新扩容的空间,而不用原来的指针变量呢,因为realloc函数可能扩容失败,会导致原来的指针被置空,原有的空间找不到,也就是偷鸡不成蚀把米
STDataType* temp = (STDataType*)realloc(ps->a,sizeof(STDataType) * newcapacity);
if (temp == NULL)
{
perror("realloc fail:");
exit(-1);
}
//扩容成功,才敢将新空间的地址交给原指针
ps->a = temp;
ps->capacity = newcapacity;
}
}
3.入栈
断言函数assert记得引用头文件<assert.h>
// 入栈
void StackPush(Stack* ps, STDataType data)
{
assert(ps);
//判断栈满,栈满则扩容
JugdeFull(ps);
ps->a[ps->top] = data;
ps->top++;
}
4.出栈
// 出栈
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);//判断栈非空
ps->top--;//出栈;
}
5.获取栈顶元素
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);//判断指针是否为空
assert(ps->top > 0);//判断栈非空
STDataType ret = ps->a[ps->top-1];
return ret;
}
6.获取栈中有效元素的个数
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
7.判断栈空
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{
assert(ps);
if (ps->top == 0)
{
return -1;
}
return 0;
}
8.销毁栈
// 销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
ps->top = ps->capacity = 0;
free(ps->a);
ps->a = NULL;
}
3.简单测试一下你的栈
void test1()
{
Stack st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
while (st.top)
{
printf("%d->", StackTop(&st));
StackPop(&st);//测试中获取完栈顶元素就要出栈依次,不然while循环就变成死循环了
}
printf("\n");
StackDestroy(&st);
}
int main()
{
test1();
return 0;
}
二、队列的实现
1.以单链表的形式创建一个队列
第一个结构体是你可以把它看作一个结点,也就是排队中的一个人。第二个结构体就是一整个队列,宏观的我们知道它的头是谁,尾是谁,有多少人组成。【数据结构就是一个抽象的概念,如果你暂时还理解不了,那一定是因为你还不够抽象~
typedef int QDataType;
// 链式结构:表示队列
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* front;
QNode* rear;
int size;
}Queue;
2.队列相关的一系列操作函数
其实每一种数据结构都有它的特点,主要的操作就是那么几个,不用怕自己记不住,用到哪个函数就写哪一个
// 初始化队列
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
bool QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
3.队列相关函数的实现
1.初始化队列
这里注意:我们初始化的是表示队列的那一个结构体!!!不是表示结点的结构体,结点我们会一个个创建并初始化,你也可以单独写一个初始化结点的,然后每开辟一个结点的空间就调用它
// 初始化队列
void QueueInit(Queue* q)
{
assert(q);
q->front = q->rear = NULL;
q->size = 0;
}
2.队尾入队列
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
QNode* newnode = (QNode*)malloc(sizeof(QNode));//新创建一个结点
//初始化结点
newnode->data = data;
newnode->next = NULL;
//判断队列是否为空,队列为空则将该入队结点看作头结点
if (q->front == NULL && q->rear == NULL)
{
q->front = newnode;
q->rear = newnode;
}
else
{
q->rear->next = newnode;
q->rear = newnode;
}
q->size++;
}
3.队头出队列
注意:出队列的时候可能需要修改尾指针,到最后一个结点的时候,要将头、尾指针都置为空
// 队头出队列
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));//判断队列不为空
if (q->front->next == NULL)//只有一个结点
{
free(q->front);
q->front = NULL;
q->rear = NULL;
}
else
{
QNode* next = q->front->next;
free(q->front);
q->front = next;
}
q->size--;
}
4.获取队头元素
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));//判断队非空
return q->front->data;
}
5.获取队尾元素
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));//判断队非空
return q->rear->data;
}
6.获取队列中有效元素个数
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
assert(q);
return q->size;
}
7.判断队列是否为空
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* q)
{
assert(q);
return q->front == NULL;
}
8.销毁队列
// 销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
q->front = q->rear = NULL;
q->size = 0;
}
三、栈实现队列
1.原理
栈实现队列,就是实现队列的相关操作,如入队、出队、获取队头元素等,同时遵守队列先进先出的规律
1.准备两个栈
2.一个栈用来专门push,一个栈用来pop
进栈的顺序是1234也就相当于进队列的顺序也是这样,因为此时的出入都是对于队列而言的。然后是出的顺序,栈1直接出就是后进先出(4321)就不符合队列的要求,所以我们需要将栈1的数据先导入栈2中,此时栈2出栈就是队列想要的顺序1234.如果有新入栈的,就让它进入栈1,栈2出栈出到空时再将栈1的数据导入栈2中。
2.代码实现
1.栈的创建和基本操作代码
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top; // 栈顶
int capacity; // 容量
}Stack;
// 初始化栈
void StackInit(Stack* ps);
//判断栈满并扩容
void JugdeFull(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)
{
ps->a = NULL;
ps->capacity = ps->top = 0;
}
//判断栈满并扩容
void JugdeFull(Stack* ps)
{
if (ps->top == ps->capacity)
{
//扩容
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* temp = (STDataType*)realloc(ps->a,sizeof(STDataType) * newcapacity);
if (temp == NULL)
{
perror("realloc fail:");
exit(-1);
}
ps->a = temp;
ps->capacity = newcapacity;
}
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{
assert(ps);
//判断栈满,栈满则扩容
JugdeFull(ps);
ps->a[ps->top] = data;
ps->top++;
}
// 出栈
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);//判断栈非空
ps->top--;//出栈;
}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);//判断指针是否为空
assert(ps->top > 0);//判断栈非空
STDataType ret = ps->a[ps->top-1];
return ret;
}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{
assert(ps);
if (ps->top == 0)
{
return -1;
}
return 0;
}
// 销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
ps->top = ps->capacity = 0;
free(ps->a);
ps->a = NULL;
}
2.封装成队列
typedef struct {
Stack PushST;
Stack PopST;
} MyQueue;
3.创建一个队列
MyQueue* myQueueCreate() {
MyQueue* Qu = (MyQueue*)malloc(sizeof(MyQueue));
StackInit(&Qu->PushST);
StackInit(&Qu->PopST);
return Qu;
}
4.队列相关操作的函数实现
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->PushST,x);
}
int myQueuePeek(MyQueue* obj) {
//判断PopST是否为空,为空则到数据,不为空则直接返回
if(StackEmpty(&obj->PopST))
{
while(StackSize(&obj->PushST))
{
//将PushST栈中的数据倒入PopST栈中
StackPush(&obj->PopST,StackTop(&obj->PushST));
StackPop(&obj->PushST);
}
}
//获取PopST栈的栈顶元素,即队列的首元素
return StackTop(&obj->PopST);
}
int myQueuePop(MyQueue* obj) {
//获取队列首元素
int ret = myQueuePeek(obj);
//并将首元素出栈
StackPop(&obj->PopST);
return ret;
}
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->PopST)&&StackEmpty(&obj->PushST);
}
void myQueueFree(MyQueue* obj) {
StackDestroy(&obj->PopST);
StackDestroy(&obj->PushST);
free(obj);
}
四、队列实现栈
1.原理
同样的道理,通过两个队列两实现一个栈
从队列1进入队列,然后将n-1个数据导入队列2中,剩下最后一个没有导入队列2的数据就是队尾元素,也就是栈顶元素。
入队列时往不空的队列放数据。出队列是往空的队列到数据,然后剩下最后一个数据直接出栈。Leetcode原题
2.代码实现
1.队列的函数实现
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);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
// 初始化队列
void QueueInit(Queue* q)
{
assert(q);
q->front = q->rear = NULL;
q->size = 0;
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
QNode* newnode = (QNode*)malloc(sizeof(QNode));
newnode->data = data;
newnode->next = NULL;
if (q->front == NULL && q->rear == NULL)
{
q->front = newnode;
q->rear = newnode;
}
else
{
q->rear->next = newnode;
q->rear = newnode;
}
q->size++;
}
// 队头出队列
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
if (q->front->next == NULL)
{
free(q->front);
q->front = NULL;
q->rear = NULL;
}
else
{
QNode* next = q->front->next;
free(q->front);
q->front = next;
}
q->size--;
}
// 获取队列头部元素
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;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* q)
{
assert(q);
return q->front == NULL;
}
// 销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
q->front = q->rear = NULL;
q->size = 0;
}
2.用队列构造一个栈
typedef struct {
Queue q1;
Queue q2;
} MyStack;
``
3.栈的函数实现
```c
int myStackPop(MyStack* obj) {
Queue* empty = &obj->q1;
Queue* nonempty = &obj->q2;
if (!QueueEmpty(&obj->q1))
{
empty = &obj->q2;
nonempty = &obj->q1;
}
while (QueueSize(nonempty) > 1)
{
QueuePush(empty, QueueFront(nonempty));
QueuePop(nonempty);
}
int top = QueueFront(nonempty);//获取最后一个数据,即栈顶(队尾)数据,并返回
QueuePop(nonempty);//将最后一个数据也出队列
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);
}
这是一个Leetcode的题目,只需要实现对应的函数。队列实现栈
五、循环队列
1.循环队列的特点
1.分配的空间是固定的
2.元素被删除之后,空间不会被释放,会被循环利用,
3.尾指针和头指针走到头就会回到1
2.循环队列的实现
循环队列是通过数组来实现的。循环队列包括三个参数,队头、队尾、有效元素个数
typedef struct {
int* a;//数组
int front;//头
int rear;//尾
int k; //有效数据个数
} MyCircularQueue;
3.循环队列判空
4.循环队列插入数据
5.循环队列删除数据
6.循环队列尾走到尾回到原点(头front同理)
回到头:rear = (rear+1)%(k+1) 。k是可以存放的结点个数,多申请的那一个结点是用来判断队列是否满的
6.循环队列判满
7.循环队列找尾元素
8.循环队列代码实现
typedef struct {
int* a;//数组
int front;//头
int rear;//尾
int k; //有效数据个数
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
//初始化循环队列
MyCircularQueue* mCQ = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
mCQ->a = (int*)malloc(sizeof(int)*(k+1));//为数组开辟k+1个空间,其中一个空间不存数据
mCQ->front = 0;
mCQ->rear = 0;
mCQ->k = k;
return mCQ;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
//判断栈满
if((obj->rear+1)%(obj->k+1)==obj->front)
{
return true;
}
return false;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
//入队列,判断栈满
if(!myCircularQueueIsFull(obj))
{
obj->a[obj->rear] = value;
obj->rear = (obj->rear+1)%(obj->k+1);
return true;
}
return false;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
//判断队列空
if(obj->rear==obj->front)
{
return true;
}
return false;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
//出队列。判断栈空
if(!myCircularQueueIsEmpty(obj))
{
obj->front = (obj->front+1)%(obj->k+1);
return true;
}
return false;
}
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);//释放动态开辟的循环队列
}
原题请看Leetcode循环队列
喜欢的朋友点个关注~