欢迎阅读新一期的c语言数据结构模块————栈和队列
🏷️专栏:C语言
📜代码仓库:c_code
🌹🌹欢迎大佬们的阅读和三连关注,顺着评论回访🌹🌹
栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

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