欢迎阅读新一期的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;//初始化大小
}