数据结构——栈与队列

1.栈

1.1概念

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

可以比作为羽毛球桶,弹夹​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

1.2栈的实现

1.2.1实现栈的选择

栈可以使用链表和数组来实现,哪种方法比较好

数组,我们对最后一个元素进行删除和插入,可以实现压栈和出栈

链表有两种,一种是单链表,另一种是双向链表

单链表我们可以把头结点当做栈顶,进行头插和头删

双向链表则没有什么限制,头插和头删,尾插,尾删都可以

那如何选择呢?

首先来看单双链表,我们既然能用单链表实现,就可以放弃双向链表,因为双向链表比单链表多一个指针,实现的时候就可以少维护一个指针,并且空间也优化了一点

其实单链表和数组的实现差不多,本章我们就只实现数组来实现栈,有兴趣的同学可以用链表来实现,而我们实现的栈和链表一样是动态的

1.2.2 准备工作

创建三个文件

复制代码
1、头文件Stack.h  是来声明接口函数,定义顺序表,将几个公共用到的库函数集合起来
2、源文件Stack.c  是用来具体实现接口
3、源文件test.c  用于接口的测试工作 ,即体的使用场景

在这里提一嘴,有人可能会问创建文件为什么要用这个名字,例如头文件Stack.h不能换成XXX.h第一是可以让我们更好的辨认这个文件是干嘛的,Stack代表栈

第二后面学习STL库的时候,这个名字就很重要了,因此大家可以先按照我这个来写,同时在下面函数的实现中,有些函数名可能不知道什么意思,没关系,有印象就行。

1.2.3基础接口实现

1.2.3.1 创建栈的结构
cpp 复制代码
typedef int STDataType;
typedef struct Stack
{
	STDataType* _a;
	int _top; // 栈顶位置
	int _capacity; // 容量
}Stack;
1.2.3.2 初始化栈
cpp 复制代码
void StackInit(Stack* ps)
{
	void StackInit(Stack* ps)
{
	assert(ps);//断言不作叙述
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = 0;
	//ps->_top = -1;
}
}

top可以选择0或者-1,这两者有什么区别

之后处理栈顶元素的时候根据自己的top处理一下就行,这里我就用top=0来处理

1.2.3.3 入栈
cpp 复制代码
void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	//扩容
	if (ps->_top == ps->_capacity)
	{
		int new = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->_a, new*sizeof(STDataType));
		if (tmp==NULL)
		{
			perror("relloc");
			return;
		}
		ps->_a = tmp;
		ps->_capacity = new;
	}
	//插入数据top=0
	ps->_a[ps->_top] = data;
	ps->_top++;
	//top=1
	//ps->_a[++ps->_top] = data;
}
1.2.3.4 出栈
cpp 复制代码
// 出栈
void StackPop(Stack* ps)
{
	assert(ps);
	ps->_top--;
}
1.2.3.5 获取栈顶元素
cpp 复制代码
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);
	return ps->_a[ps->_top - 1];//top=0
	//return ps->_a[ps->_top ];top=-1;
}
1.2.3.6 获取栈中有效元素个数
cpp 复制代码
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->_a[ps->_top ];//top=0
	//return ps->_a[ps->_top+1 ];top=-1;
}
1.2.3.7 检测栈是否为空
cpp 复制代码
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
	assert(ps);
	//top=0
	if (ps->_top == 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
	//top=-1;
	/*if (ps->_top == -1)
	{
		return 1;
	}
	else
	{
		return 0;
	}*/
}
1.2.3.8 销毁栈
cpp 复制代码
// 销毁栈
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->_a);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = 0;
}

2.队列

2.1概念

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

可以当做我们日常生活中排队买东西的方式

2.2队列的实现

2.2.1实现队列的选择

和栈一样,队列有三种选择:数组,单双链表,那根据队列的先进先出的原则,来看看那个结构比较好

先来看看数组,我们发现,每次数组进行出队列的操作时,数组内的元素总要往前移动,效率较低

而链表就不需要移动数据,出队列头删,入队列尾插就行了,同样在创建队列的时候只需要单链表就行了,可以节约一部分空间

2.2.2准备工作

和链表一样创建三个文件

1、头文件Queue.h 是来声明接口函数,定义顺序表,将几个公共用到的库函数集合起来 2、源文件Queue.h 是用来具体实现接口

3、源文件test.c 用于接口的测试工作 ,即体的使用场景

2.2.3基础接口实现

2.2.3.1创建队列结构
cpp 复制代码
typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* _pNext;
	QDataType _data;
}QNode;

当我们实现这个结构的时候,我们每次入队列的时候都要找到最后一个节点,我们写入队列函数时要传入三个参数,分别是头结点,尾结点,数据,尾插为什么要头结点那,当队列没有数据是,为NULL,就需要头结点和尾结点维护

、、

而且实现函数时,要传二级指针,这样才能改变实参,这样实现函数非常麻烦,此时我们可以用一个结构体来存储队列的头尾节点

cpp 复制代码
typedef int QDataType;
typedef struct QListNode// 链式结构:表示队列
{
	struct QListNode* _pNext;
	QDataType _data;
}QNode;
typedef struct Queue// 队列的结构
{
	 QNode * phead;//头结点
	 QNode* ptail;//尾结点
     int size;//数据个数
}Queue;

这样我们在入队列函数传参数的时候就可以只用传这个结构体的指针,以及数据,这样不仅可以少传一个参数,又避免了二级指针

2.2.3.2 队尾入队列
cpp 复制代码
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	//创建链表
	QNode* newnode= (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}
	newnode->_pNext = NULL;
	newnode->_data = data;
	//没有数据时
	if ( q->ptail == NULL)
	{
		q->phead = q->ptail = newnode;
		
	}
	//有数据时
	else
	{
		q->ptail->_pNext = newnode;
		q->ptail = newnode;
	}
	q->size++;
}
2.2.3.3队头出队列
cpp 复制代码
// 队头出队列
void QueuePop(Queue* q)
{
	assert(q);
	assert(q->size != 0);
	//只有一个节点时
	if (q->phead->_pNext == NULL)
	{
		free(q->phead);
		q->phead = q->ptail = NULL;
	}
	//多个节点
	else
	{
		QNode* node = q->phead->_pNext;
		free(q->phead);
		q->phead = node;
	}
	q->size--;
}
2.2.3.4获取队列头部元素
cpp 复制代码
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
	assert(q);
    assert(q->phead);
	return q->phead->_data;
}
2.2.3.5获取队列队尾元素
cpp 复制代码
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
	assert(q);
    assert(q->ptail);
	return q->ptail->_data;
}
2.2.3.6获取队列中有效元素个数
cpp 复制代码
int QueueSize(Queue* q)
{
	assert(q);
	return q->size;
}
2.2.3.7检测队列是否为空
cpp 复制代码
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);
	return q->size == 0;
}
2.2.3.7销毁队列
cpp 复制代码
// 销毁队列
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->phead;
	while (cur)
	{
		QNode* next = cur->_pNext;
		free(cur);
		cur = next;
	}
	q->size = 0;
}
相关推荐
朝阳5817 分钟前
Rust项目GPG签名配置指南
开发语言·后端·rust
朝阳5818 分钟前
Rust实现高性能目录扫描工具ll的技术解析
开发语言·后端·rust
程高兴10 分钟前
基于Matlab的车牌识别系统
开发语言·matlab
YuforiaCode10 分钟前
第十三届蓝桥杯 2022 C/C++组 修剪灌木
c语言·c++·蓝桥杯
牛马baby34 分钟前
Java高频面试之并发编程-07
java·开发语言·面试
CodeWithMe40 分钟前
【C++】STL之deque
开发语言·c++
SsummerC41 分钟前
【leetcode100】零钱兑换Ⅱ
数据结构·python·算法·leetcode·动态规划
电子云与长程纠缠1 小时前
Unreal Niagara制作SubUV贴图翻页动画
学习·ue5·编辑器·贴图·niagara
炯哈哈1 小时前
【上位机——MFC】运行时类信息机制
开发语言·c++·mfc·上位机
好易学·数据结构2 小时前
可视化图解算法:二叉树的最大深度(高度)
数据结构·算法·二叉树·最大高度·最大深度·二叉树高度·二叉树深度