个人主页:李仙桎
🔥 个人专栏: 《数据结构与算法》
⛺️生活的理想,就是为了理想的生活!

⛺️前言:各位铁汁们好啊!!!,今天继续学习数据结构相关的内容,后续不断更新数据结构有关知识内容!!希望各位铁汁多多支持!这一章节主要是《数据结构》第三章 栈和队列 栈的相关知识总结。
1、栈的定义
栈(stack)是只允许一端进行插入和删除的线性表
栈是一种特殊的线性数据结构,仅支持在一个位置进行添加元素(称为"入栈"或"push"操作)和移除元素(称为"出栈"或"pop"操作)的操作。这个位置就是栈顶(Top)。由于栈是后进先出(LIFO, Last In First Out)的数据结构,最后一个添加到栈中的元素将是第一个被移除。


栈进出栈的变化形式
首先提一个问题,最先进栈的元素,是不是一定最后出栈呢?
答案是不一定的,在不是所有元素都进栈的情况下,先进去的元素也可以出栈,保证是栈顶元素出栈就可以
举例,如果我们有a、b、c、d、e三个数字一次进栈,会有哪些出栈次序呢?
第一种:a、b、c、d、e进,再e、d、c、b、a出,出栈次序为edcba
第二种:a进,a出,b进,c进,d进,e进。然后e、d、c、b的次序出,出栈次序为aedcb
第三种,a进,b进,b出,c进,d进,e进,然后e、d、c、a出,出栈顺序为bedca
......
栈的顺序存储结构的有关操作
对于栈来讲,线性表的操作特性它都具备,由于它的特殊性,特别是插入和删除操作,我们改名为push和pop
线性表是用数组来实现的,对于栈这一种只能一头插入的线性表来说,下表为0的一段作为栈底

在栈的实现中,top变量一般用来指示栈顶元素的位置。对于一个空栈来说,不存在任何元素,因此没有一个合理的位置可以被称为栈顶。在这种情况下,需要一个特殊的值来表示栈是空的
在进行入栈和出栈操作时,top的更新逻辑变得简单直接。例如,每当添加一个新元素到栈中时,先将top加1(这将把top从-1改为0,表示第一个元素的位置),然后在top对应的位置上存放新元素


注意++S.top和S.top++的区别:++S.top和S.top++都是将S.top进行+1的操作,但是S.data[++S.top]是先进行+1操作,然后在进行复制操作,但是S.data[S.top++]却是先进行赋值操作,然后在进行+1操作。

栈的链式存储结构的有关操作
讲完了栈的顺序存储,我们接着来看栈的链式存储
当使用链表实现链式栈时,通常选择链表的头部作为栈顶,因为这种方法更高效、实现也更简单:
- 在链表头部插入或删除节点只需要O(1)的时间复杂度,因为这些操作不需要遍历整个链表。这对于栈操作(即push和pop操作)非常理想,因为它们也应该是O(1)的时间复杂度
- 链表有头指针,栈有顶部指针,可以做到合二为一

链表的创建
typedef int STDataType;
typedef struct StackNode {
STDataType data;
struct StackNode* next;
} StackNode;
typedef struct LinkedStack{
StackNode* top;
int size;
} LinkedStack;
初始化
初始化一个空栈,只需要将栈顶指针设置为NULL,栈的大小设置为0
void Initialize(LinkedStack* stack) {
stack->top = NULL;
stack->size = 0;
}
压栈和出栈
int Push(LinkedStack* stack, int x) {
StackNode* Node = (StackNode*)malloc(sizeof(StackNode));
if (newNode == NULL) {
printf("Memory allocation failed\n");
return -1; // 使用-1表示错误情况,实际使用中应考虑其他错误处理方式
}
newNode->data = x ;
newNode->next = stack->top; // 新节点的下一个节点就是当前的栈顶
stack->top = newNode; // 更新栈顶为新节点
stack->size++;
return 1;
}
int Pop(LinkedStack* stack) {
if (stack->top == NULL) { // 检查栈是否为空
printf("Stack is empty\n");
return -1; // 使用-1表示错误情况,实际使用中应考虑其他错误处理方式
}
StackNode* temp = stack->top; // 临时保存栈顶节点
int data = temp->data; // 获取栈顶数据
stack->top = temp->next; // 更新栈顶指针为下一个节点
free(temp); // 释放原栈顶节点的内存
stack->size--;
return data; // 返回栈顶数据
}
推入新元素需要创建一个新的节点,并将其插入到链表的头部。弹出栈顶元素先要检查栈是否为空。如果不为空,将栈顶节点从链表中移除,并释放它所占用的内存。
检查栈是否为空
检查链式栈是否为空也很简单,只需检查栈顶指针是否为NULL。
int IsEmpty(LinkedStack* stack) {
return stack->top == NULL;
}