【C语言数据结构(基础版)】第四站:栈和队列

目录

一.栈的表示和实现

1.栈的概念及结构

 2.栈的实现

 二、栈的实现

1.栈的声明和定义

2.栈的初始化

3.栈的销毁

4.入栈

5.出栈

6.返回栈顶元素

7.返回栈的元素个数

8.栈是否为空

9.测试

 三、栈的完整代码

四、队列的表示和实现

1.队列的概念和结构

2.队列的实现

五、队列的实现

1.队列的声明和定义

2.队列的初始化

3.队列的销毁

4.队列的插入

5.队列的删除

6.队列的判空

7.取出队头的数据

8.取出队尾的数据

9.队列数据的个数

10.测试

 六、队列完整代码

总结


一.栈的表示和实现

1.栈的概念及结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵循后进先出LIFO(Last in First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶

出栈:栈的删除操作叫做出栈。出数据也在栈顶

 2.栈的实现

从上面我们也可以看出来,栈的实现一般可以使用数组或者链表实现,相对而言,其实数组的结构实现更优一些,因为数组在尾上插入数据的代价比较小

 二、栈的实现

1.栈的声明和定义

根据我们上面的分析,我们决定使用顺序表来实现栈。所以它的声明如下

typedef int STDateType;
typedef struct Stack
{
	STDateType* a;
	int top;
	int capacity;
}Stack;

2.栈的初始化

它的声明如下

//栈的初始化
void StackInit(Stack* ps);

它的函数实现也很简单

//栈的初始化
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = (STDateType*)malloc(4 * sizeof(STDateType));
	if (ps->a == NULL)
	{
		printf("malloc is fail\n");
		exit(-1);
	}
	ps->capacity = 4;
	ps->top = 0;
}

注意这里的top可以为0也可以为-1

这两种其实也是有区别的,

如果top初始化为0的化,那就意味着top指向栈顶元素的下一个

如果top初始化欸-1的话,那么意味着top指向栈顶元素

3.栈的销毁

创建好那么必要要有销毁

销毁的声明为

//栈的销毁
void StackDestory(Stack* ps);

实现为

//栈的销毁
void StackDestory(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

4.入栈

这个也非常简单,函数声明为

//入栈
void StackPush(Stack* ps, STDateType x);

实现为

//入栈
void StackPush(Stack* ps, STDateType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		//扩容
		STDateType* tmp = (STDateType*)realloc(ps->a, sizeof(STDateType) * 2 * ps->capacity);
		if (tmp == NULL)
		{
			printf("realloc is fail\n");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity *= 2;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

5.出栈

函数声明为

//出栈
void StackPop(Stack* ps);

函数实现为

//出栈
void StackPop(Stack* ps)
{
	assert(ps);
	//栈为空了,还想要继续删除直接报错
	assert(ps->top > 0);
	ps->top--;
}

6.返回栈顶元素

这是函数声明

//取出栈顶元素
STDateType StackTop(Stack* ps);

这是函数实现

//取出栈顶元素
STDateType StackTop(Stack* ps)
{
	assert(ps);
	//栈为空,还继续调用的话直接报错
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

7.返回栈的元素个数

这是函数声明

//栈的元素个数
int StackSize(Stack* ps);

这是函数实现

//栈的元素个数
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;
}

8.栈是否为空

这是函数声明

//栈是否为空
bool StackEmpty(Stack* ps);

这是函数实现

//栈是否为空
bool StackEmpty(Stack* ps)
{
	assert(ps);
	return (ps->top == 0);
}

9.测试

#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"

int main()
{
	Stack s;
	StackInit(&s);
	StackPush(&s, 1);
	StackPush(&s, 2);
	StackPush(&s, 3);

	printf("%d ", StackTop(&s));
	StackPop(&s);

	printf("%d ", StackTop(&s));
	StackPop(&s);


	StackPush(&s, 4);
	StackPush(&s, 5);

	while (!StackEmpty(&s))
	{
		printf("%d ", StackTop(&s));
		StackPop(&s);
	}
	StackDestory(&s);
}

运行结果为

 三、栈的完整代码

Test.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"

int main()
{
	Stack s;
	StackInit(&s);
	StackPush(&s, 1);
	StackPush(&s, 2);
	StackPush(&s, 3);

	printf("%d ", StackTop(&s));
	StackPop(&s);

	printf("%d ", StackTop(&s));
	StackPop(&s);


	StackPush(&s, 4);
	StackPush(&s, 5);

	while (!StackEmpty(&s))
	{
		printf("%d ", StackTop(&s));
		StackPop(&s);
	}
	StackDestory(&s);
}

Stack.h文件

#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>

typedef int STDateType;
typedef struct Stack
{
	STDateType* a;
	int top;
	int capacity;
}Stack;

//栈的初始化
void StackInit(Stack* ps);
//栈的销毁
void StackDestory(Stack* ps);
//入栈
void StackPush(Stack* ps, STDateType x);
//出栈
void StackPop(Stack* ps);
//取出栈顶元素
STDateType StackTop(Stack* ps);
//栈的元素个数
int StackSize(Stack* ps);
//栈是否为空
bool StackEmpty(Stack* ps);

Stack.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"

//栈的初始化
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = (STDateType*)malloc(4 * sizeof(STDateType));
	if (ps->a == NULL)
	{
		printf("malloc is fail\n");
		exit(-1);
	}
	ps->capacity = 4;
	ps->top = 0;
}
//栈的销毁
void StackDestory(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}
//入栈
void StackPush(Stack* ps, STDateType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		//扩容
		STDateType* tmp = (STDateType*)realloc(ps->a, sizeof(STDateType) * 2 * ps->capacity);
		if (tmp == NULL)
		{
			printf("realloc is fail\n");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity *= 2;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
//出栈
void StackPop(Stack* ps)
{
	assert(ps);
	//栈为空了,还想要继续删除直接报错
	assert(ps->top > 0);
	ps->top--;
}
//取出栈顶元素
STDateType StackTop(Stack* ps)
{
	assert(ps);
	//栈为空,还继续调用的话直接报错
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}
//栈的元素个数
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;
}
//栈是否为空
bool StackEmpty(Stack* ps)
{
	assert(ps);
	return (ps->top == 0);
}

四、队列的表示和实现

1.队列的概念和结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First in First Out)入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头

2.队列的实现

队列也可以数组和链表的结构实现,但使用链表的结构更优一些,因为如果使用数组的结构,出队列在数组的头上出数据,效率会比较低.

五、队列的实现

1.队列的声明和定义

我们想要使用链表的结构来实现队列,但是由于单链表的尾插过于麻烦,不妨我们直接声明头节点和尾结点来控制队列

typedef int QDateType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDateType date;
}QNode;
typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

如上所示,我们先定义了一个队列结点的结构体,里面包含了指向下一个结点的指针和一个队列的数据。然后我们定义了一个队列结构体,我们主要是使用队列这个结构体,它里面有头结点和尾结点,有头有尾刚好确定一个队列。

2.队列的初始化

接下来让我们先来实现一下队列的初始化

函数声明为

//队列初始化
void QueueInit(Queue* pq);

实现为

//队列的初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}

其实对这个实现,建议与前面的一些LeetCode题目和单链表的实现进行对比,我们可以看出来这里的妙处。

3.队列的销毁

有始有终,我们来实现一下队列的销毁

//队列的销毁
void QueueDestory(Queue* pq);

函数实现为

//队列的销毁
void QueueDestory(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

4.队列的插入

函数声明为

//队列的插入
void QueuePush(Queue* pq, QDateType x);

函数实现为

//队列的插入
void QueuePush(Queue* pq, QDateType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc is fail\n");
		exit(-1);
	}
	//对新节点进行初始化
	newnode->date = x;
	newnode->next = NULL;

	//对新结点进行连接
	if (pq->head == NULL)
	{
		//头结点是空的,也就是第一个数据的插入
		pq->head = pq->tail = newnode;
	}
	else
	{
		//非第一个数据的插入
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

5.队列的删除

函数声明为

//队列的删除
void QueuePop(Queue* pq);

函数实现为

//队列的删除
void QueuePop(Queue* pq)
{
	assert(pq);
	//确保队列不是空的队列
	assert(pq->head);
	//如果只有一个结点,防止tail形成野指针
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	//不是只有一个结点
	else
	{
		//保存第二个队列结点
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
} 

6.队列的判空

 函数声明

//判断队列是否为空
bool QueueEmpty(Queue* pq);

函数实现

//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

7.取出队头的数据

函数声明

//取出队头的数据
QDateType QueueFront(Queue* pq);

函数实现

//取出队头的数据
QDateType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->head->date;
}

8.取出队尾的数据

函数声明

//取出队尾的数据
QDateType QueueBack(Queue* pq);

函数实现

//取出队尾的数据
QDateType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->tail->date;
}

9.队列数据的个数

函数声明

//取出数据的个数
int QueueSize(Queue* pq);

 函数实现

//取出数据的个数
int QueueSize(Queue* pq)
{
	assert(pq);
	int size = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		cur = cur->next;
		size++;
	}
	return size;
}

10.测试


void TestQueue()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");
	QueueDestory(&q);
}
int main()
{
	//TestStack();
	TestQueue();
}

运行结果为

 六、队列完整代码

 Test.c

void TestQueue()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");
	QueueDestory(&q);
}
int main()
{
	//TestStack();
	TestQueue();
}

Queue.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include<assert.h>

typedef int QDateType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDateType date;
}QNode;
typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

//队列初始化
void QueueInit(Queue* pq);
//队列的销毁
void QueueDestory(Queue* pq);
//队列的插入
void QueuePush(Queue* pq, QDateType x);
//队列的删除
void QueuePop(Queue* pq);
//取出队头的数据
QDateType QueueFront(Queue* pq);
//取出队尾的数据
QDateType QueueBack(Queue* pq);
//取出数据的个数
int QueueSize(Queue* pq);
//判断队列是否为空
bool QueueEmpty(Queue* pq);

Queue.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
//队列的初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}
//队列的销毁
void QueueDestory(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}
//队列的插入
void QueuePush(Queue* pq, QDateType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc is fail\n");
		exit(-1);
	}
	//对新节点进行初始化
	newnode->date = x;
	newnode->next = NULL;

	//对新结点进行连接
	if (pq->head == NULL)
	{
		//头结点是空的,也就是第一个数据的插入
		pq->head = pq->tail = newnode;
	}
	else
	{
		//非第一个数据的插入
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}
//队列的删除
void QueuePop(Queue* pq)
{
	assert(pq);
	//确保队列不是空的队列
	assert(pq->head);
	//如果只有一个结点,防止tail形成野指针
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	//不是只有一个结点
	else
	{
		//保存第二个队列结点
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
} 
//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}
//取出队头的数据
QDateType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->head->date;
}
//取出队尾的数据
QDateType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->tail->date;
}
//取出数据的个数
int QueueSize(Queue* pq)
{
	assert(pq);
	int size = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		cur = cur->next;
		size++;
	}
	return size;
}

总结

本节实现了栈和队列的实现,难度不大,希望大家都能学会

如果对你有帮助,不要忘记点赞加收藏哦!!!

想获得更多优质的内容,一定要记得关注我哦!!!

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 24
    评论
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青色_忘川

你的鼓励是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值