欢迎阅读新一期的c语言数据结构模块————栈和队列
✒️个人主页:-_Joker_-
🏷️专栏:C语言
📜代码仓库:c_code
🌹🌹欢迎大佬们的阅读和三连关注,顺着评论回访🌹🌹
文章目录
- 栈 
   - 一、栈的概念        
     - 1.什么是栈
- 2.栈的基本操作
 
- 二、栈的实现 
     - 1.定义栈的结构
- 2.栈的初始化
- 3.栈的判空
- 4.入栈
- 5.出栈
- 6.获取栈顶元素
- 7.栈的销毁
 
 
- 一、栈的概念        
     
- 队列 
   - 一、队列的概念 
     - 1.什么是队列
- 2.队列的基本操作
 
- 二、队列的实现 
     - 1.定义队列的结构
- 2.队列初始化
- 3.队列判空
- 4.入队
- 5.出队
- 6.获取队首元素
- 7.队列的销毁
 
 
- 一、队列的概念 
     
栈
一、栈的概念
1.什么是栈
栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

栈顶(Top): 线性表允许进行插入删除的那一端。
栈底(Bottom): 固定的,不允许进行插入和删除的另一端。
空栈: 不含任何元素的空表。
因为其后进先出(Last In First Out)的特性,栈又称为的线性表,简称LIFO结构
2.栈的基本操作
栈的基本操作通常都有以下几种:
- InitStack(&Stack):初始化一个空栈S。
- StackEmpty(&Stack):判断一个栈是否为空,若栈为空则返回true,否则返回false。
- StackPush(&Stack, x):进栈(栈的插入操作),若栈S未满,则将x加入使之成为新栈顶。
- StackPop(&Stack):出栈(栈的删除操作),若栈S非空,则返回一个提示.
- GetTop(&Stack):读栈顶元素。
- DestroyStack(&Stack):栈销毁,并释放S占用的存储空间(“&”表示引用调用)。
二、栈的实现
1.定义栈的结构
栈又分为顺序栈和链式栈,这里我们以顺序栈为例
首先想要实现一个栈,我们需要了解如何创建一个栈的结构,这里我们可以用结构体来定义,有两种方式:
①
#define N 10//定义栈的大小
struct Stack
{
	int a[N];
	int top;//栈顶
};②
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;//定义一个栈
	int top;//栈顶
	int capacity;//栈的大小
}ST;第一种结构是用一个宏定义常量定义的栈的大小,这种用静态开辟的空间存在一些瑕疵,如果栈的空间大小太小需要成倍的扩容,很容易造成空间的浪费,所以我们优先采用方法②。
这种方法的好处在于我们可以动态开辟空间,如果空间不够就多开一个空间,这样就可以避免空间的浪费。
2.栈的初始化
void InitStack(ST* ps)
{
	assert(ps);//断言
	ps->a = NULL;//将指针置为空
	ps->capacity = 0;//初始化大小
	ps->top = 0;
}3.栈的判空
bool StackEmpty(ST* ps)
{
	assert(ps);//断言
	return ps->top == 0;//栈顶为0则为空
}4.入栈
入栈的具体流程如图
入栈首先判断空间大小,若已满则需要扩容,然后从栈底向上逐个插入
入栈操作如下
void StackPush(ST* ps, STDataType x)
{
	assert(ps);//断言
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
        //扩容    
		if (tmp == NULL)//判断空间是否开辟成功
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
	ps->a[ps->top] = x;//插入元素
	ps->top++;//栈顶移动
}
5.出栈
出栈操作如图
出栈首先判断是否为空,若为空则返回一个提示,若不为空则只需要直接将栈顶向下移动即可达到出栈的效果。
出栈操作如下;
void StackPop(ST* ps)
{
	assert(ps);//断言
	assert(ps->top > 0);//判断空
	--ps->top;//栈顶移动
}6.获取栈顶元素
这里需要注意top的位置,如果top的指向是栈顶元素的话则只需要return a[ps->top]即可,由于我这里的top是指向栈顶元素的下一个位置,所以需要top-1才可以获取到栈顶元素.
STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);//判断为空
	return ps->a[ps->top - 1];//返回栈顶元素
}7.栈的销毁
void StackDestroy(ST* ps)
{
	assert(ps);
	free(ps->a);//释放栈的空间
	ps->a = NULL;//将指针置空,防止野指针产生
	ps->top = ps->capacity = 0;
}队列
一.队列的概念
1.什么是队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
队首(front): 线性表允许进行插入删除的那一端。
队尾(rear): 固定的,不允许进行插入和删除的另一端。
空队列: 不含任何元素的空表。
因为其先进先出(First In First Out)的特性,队列又称为的线性表,简称FIFO结构
2.队列的基本操作
- QueueInit(&Q):初始化队列,构造一个空队列Q。
- QueueEmpty(Q):判队列空,若队列Q为空返回true,否则返回false。
- QueuePush(&Q, x):入队,若队列Q未满,将x加入,使之成为新的队尾。
- QueuePop(&Q):出队,若队列Q非空,删除队头元素。
- QueueFront(&Q):读队头元素。
- QueueDestroy(&Q): 队列销毁。
二.队列的实现
队列同样拥有两种实现方式(顺序结构和链式结构),但是用顺序结构实现队列在出列的情况比较复杂,所以这里我们以链式结构来实现队列
1.定义队列的结构
和栈类似,队列的结构可以这样定义
typedef int QDataType;
typedef struct QueueNode//队列节点
{
	struct QueueNode* next;
	QDataType data;
}QNode;
typedef struct Queue//队列结构
{
	QNode* head;//队首
	QNode* tail;//队尾
	int size;
}Que;2.队列初始化
void QueueInit(Que* pq)
{
	assert(pq);//断言
	pq->head = pq->tail = NULL;//头尾指针置空
	pq->size = 0;//大小初始化
}3.队列判空
bool QueueEmpty(Que* pq)
{
	assert(pq);//断言
	return pq->head == NULL;//头指针为空则为空
}4.入队
入队操作如图
入队新开一个节点,将元素插入新的节点,然后判断尾指针是否指向空,若为空则将头尾指针指向新的节点,若不为空则将新的节点作为队尾。
入队实现:
void QueuePush(Que* pq, QDataType x)
{
	assert(pq);//断言
	QNode* newnode = (QNode*)malloc(sizeof(QNode));//开一个新节点
    if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;//新节点赋值
	newnode->next = NULL;
	if (pq->tail == NULL)//空队列
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	pq->size++;
}5.出队
出队操作和入队操作类似,出队首先判空,若队列为空则返回一个提示,若不为空需要将队首节点指向下一个节点并释放第一个节点。若队首的下一个元素为空,说明改队列只有一个节点,所以需要将头尾指针置空防止野指针的产生。
出队操作如下
void QueuePop(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));//判空
	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;//将新开的节点赋给新队首
	}
	pq->size--;//队列大小-1
}
6.读取队首元素
QDataType QueueFront(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));//判空
	return pq->head->data;//取队首元素
}7.队列销毁
void QueueDestroy(Que* pq)
{
	assert(pq);
	QNode* cur = pq->head;//创建一个节点存储队首
	while (cur)//
	{
		QNode* next = cur->next;//创建一个中间节点
		free(cur);//从队首一个一个删除
		cur = next;
	}
	pq->head = pq->tail = NULL;//删完所有节点将指针置空
	pq->size = 0;//初始化大小
}























