《数据结构》C语言版 (清华严蔚敏考研版) 第三章 栈和队列 栈相关知识梳理与总结

个人主页李仙桎

🔥 个人专栏: 《数据结构与算法》

⛺️生活的理想,就是为了理想的生活!

⛺️前言:各位铁汁们好啊!!!,今天继续学习数据结构相关的内容,后续不断更新数据结构有关知识内容!!希望各位铁汁多多支持!这一章节主要是《数据结构》第三章 栈和队列 栈的相关知识总结。

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;
}
相关推荐
AI软著研究员9 小时前
程序员必看:软著不是“面子工程”,是代码的“法律保险”
算法
FunnySaltyFish9 小时前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
颜酱10 小时前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
地平线开发者1 天前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶
董董灿是个攻城狮1 天前
大模型连载2:初步认识 tokenizer 的过程
算法
地平线开发者1 天前
地平线 VP 接口工程实践(一):hbVPRoiResize 接口功能、使用约束与典型问题总结
算法·自动驾驶
罗西的思考1 天前
AI Agent框架探秘:拆解 OpenHands(10)--- Runtime
人工智能·算法·机器学习
HXhlx1 天前
CART决策树基本原理
算法·机器学习
Wect1 天前
LeetCode 210. 课程表 II 题解:Kahn算法+DFS 双解法精讲
前端·算法·typescript
颜酱1 天前
单调队列:滑动窗口极值问题的最优解(通用模板版)
javascript·后端·算法