C语言栈和队列大大大礼包【栈实现队列、队列实现栈、再附赠一个循环队列,啊啊啊啊】

前言【必看】

学完栈和队列的宝子欢迎参观。。。【没有学过的宝子可能不太理解,因为注释少,实现多。【主要是懒(滚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循环队列

喜欢的朋友点个关注~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值