【王道数据结构】第三章 | 栈和队列

news2024/12/23 4:35:12

目录

一、栈stack

基本概念

基本操作

存储结构​​​​​​​

二、队列Queue

基本概念

队列的基本操作

存储结构

三、栈的应用

栈在括号匹配中的应用

栈在表达式求值中的应用​编辑

栈在递归中的应用


一、栈stack

基本概念

  • 只允许在一端(栈顶top)进行插入或删除操作的受限的线性表
  • 后进先出(LIFO)

基本操作

  • InitStack(&S): 初始化一个空栈SStackEmpty(S):判断一个栈是否为空,若栈为空则返回true,否则返回false。
  • Push(&S,x):进栈,若S未满,则将x加入使之成为新栈顶。
  • Pop(&S,&x): 出栈,若非空,则弹出栈顶元素,并用x返回。
  • GetTop(S,&x): 读栈顶元素,若栈非空则用x返回栈顶元素
  • ClearStack(&S):):销毁栈,并释放S占用的内存空间。

存储结构

【顺序存储结构(顺序栈)】

#define MaxSize 10              //定义栈中元素的最大个数

typedef struct{
    ElemType data[MaxSize];     //静态数组存放栈中元素
    int top;                    //栈顶元素
}SqStack;

void testStack(){
    SqStack S;                 //声明一个顺序栈(分配空间)
                               //连续的存储空间大小为 MaxSize*sizeof(ElemType)
}

顺序栈的基本操作

#define MaxSize 10         //定义栈中元素的最大个数

typedef struct{
    ElemType data[MaxSize];       //静态数组存放栈中元素
    int top;                      //栈顶元素
}SqStack;

//初始化栈
void InitStack(SqStack &S){
    S.top = -1;                   //初始化栈顶指针
}

//判栈空
bool StackEmpty(SqStack S){
    if(S.top == -1)      //栈空
        return true;
    else                 //栈不空
        return false;
}

//新元素进栈
bool Push(SqStack &S, ElemType x){
    if(S.top == MaxSize - 1)        //栈满
        return false;
    
    S.top = S.top + 1;    //指针先加1
    S.data[S.top] = x;    //新元素入栈

    /*
    S.data[++S.top] = x;
    */
    return true;
}

//出栈
bool Pop(SqStack &x, ElemType &x){
    if(S.top == -1)          //栈空
        return false;
    
    x = S.data[S.top];       //先出栈
    S.top = S.top - 1;       //栈顶指针减1
    return true;

    /*
    x = S.data[S.top--];
    */

    //只是逻辑上的删除,数据依然残留在内存里

}

//读栈顶元素
bool GetTop(SqStack S, ElemType &x){
    if(S.top == -1)
        return false;
    
    x = S.data[S.top];      //x记录栈顶元素
    return true; 
}


void testStack(){
    SqStack S;       //声明一个顺序栈(分配空间)
    InitStack(S);
    //...
}
  • 进栈操作 :栈不满时,栈顶指针先加1,再送值到栈顶元素。S.data[S.top++] = x;
  • 出栈操作:栈非空时,先取栈顶元素值,再将栈顶指针减1。`x = S.data[–S.top];
  • 栈空条件:S.top==-1
  • 栈满条件:S.top==MaxSize-1
  • 栈长:S.top+1

共享栈】 

  • 共享栈--特殊的顺序栈
  • 将栈底设计在共享空间的两端,栈顶向中间靠拢
#define MaxSize 10         //定义栈中元素的最大个数

typedef struct{
    ElemType data[MaxSize];       //静态数组存放栈中元素
    int top0;                     //0号栈栈顶指针
    int top1;                     //1号栈栈顶指针
}ShStack;

//初始化栈
void InitSqStack(ShStack &S){
    S.top0 = -1;        //初始化栈顶指针
    S.top1 = MaxSize;   
}

【链式存储结构(链栈)】

  • 定义:采用链式存储的栈称为链栈。
  • 优点:链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。
  • 特点:进栈和出栈都只能在栈顶一端进行(链头作为栈顶)

链表的头部作为栈顶,意味着:

  • 1. 在实现数据"入栈"操作时,需要将数据从链表的头部插入;
  • 2. 在实现数据"出栈"操作时,需要删除链表头部的首元节点;

因此,链栈实际上就是一个只能采用头插法插入或删除数据的链表;
栈的链式存储结构可描述为:

typedef struct Linknode{
    ElemType data;              //数据域
    struct Linknode *next;      //指针域
}*LiStack;                      //栈类型的定义

带头结点的链栈基本操作:

#include<stdio.h>

struct Linknode{
    int data;             //数据域
    Linknode *next;       //指针域
}Linknode,*LiStack;   

typedef Linknode *Node;   //结点结构体指针变量
typedef Node List;        //结点结构体头指针变量

//1. 初始化
void InitStack(LiStack &L){   //L为头指针
    L = new Linknode; 
    L->next = NULL;
}

//2.判栈空
bool isEmpty(LiStack &L){
    if(L->next == NULL){
        return true;
    }
    else
        return false;
}

//3. 进栈(:链栈基本上不会出现栈满的情况)
void pushStack(LiStack &L, int x){
    Linknode s;          //创建存储新元素的结点
    s = new Linknode;
    s->data = x;

    //头插法
    s->next = L->next;
    L->next = s;
}

//4.出栈
bool popStack(LiStack &L, int &x){
    Linknode s;
    if(L->next == NULL) //栈空不能出栈
        return false;
    
    s = L->next;
    x = s->data;
    L->next = L->next->next;
    delete(s);

    return true;
}



不带头结点的链栈基本操作:

#include<stdio.h>

struct Linknode{
    int data;             //数据域
    Linknode *next;       //指针域
}Linknode,*LiStack;   

typedef Linknode *Node;   //结点结构体指针变量
typedef Node List;        //结点结构体头指针变量

//1.初始化 
void initStack(LiStack &L){
    L=NULL;
}

//2.判栈空
bool isEmpty(LiStack &L){
    if(L == NULL)
        return true;
    else
        teturn false;
}

//3.进栈
void pushStack(LiStack &L, int x){
    Linknode s;          //创建存储新元素的结点
    s = new Linknode;

    s->next = L;
    L = s;
}

//4.出栈
bool popStack(LiStack &L, int &x){
    Linknode s; 
    if(L = NULL)     //栈空不出栈
        return false;

    s = L;
    x = s->data;
    L = L->next;
    delete(s);
    
    return true;
}

二、队列Queue

基本概念

  • 只允许在表的一端(队尾)插入,表的另一端(队头)进行删除操作的受限的线性表。
  • 先进先出FIFO
  • 队列的基本操作

队列的基本操作

  • InitQueue(&Q): 初始化队列,构造一个空队列Q.
  • QueueEmpty(Q):判队列空,若队列Q为空返回true,否则返回false.
  • EnQueue(&Qx):入队,若队列Q未满,则将x加入使之成为新的队尾.
  • DeQueue(&Q&x):出队,若队列Q非空,则删除队头元素,并用x返回.
  • GetHead(Q&x):读队头元素,若队列Q非空则用x返回队头元素
  • ClearQueue(&Q):销毁队列,并释放队列Q占用的内存空间。

存储结构

  • 队头指针:指向队头元素
  • 队尾指针:指向队尾元素的下一个位置
  1. 队列存储的基本操作
//队列的顺序存储类型
# define MaxSize 10;     //定义队列中元素的最大个数
typedef struct{
    ElemType data[MaxSize];   //用静态数组存放队列元素
                              //连续的存储空间,大小为——MaxSize*sizeof(ElemType)
    int front, rear;          //队头指针和队尾指针
}SqQueue;

//初始化队列
void InitQueue(SqQueue &Q){
    //初始化时,队头、队尾指针指向0
    Q.rear = Q.front = 0;
}

void test{
    SqQueue Q;                //声明一个队列
    InitQueue(Q);
    //...
}

// 判空
bool QueueEmpty(SqQueue 0){
    if(Q.rear == Q.front)    //判空条件后
        return true;
    else 
        return false;
}

2. 循环队列
定义:将循环队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。
基本操作

a%b == a除以b的余数

初始:Q.front = Q.rear = 0;

队首指针进1:Q.front = (Q.front + 1) % MaxSize

队尾指针进1:Q.rear = (Q.rear + 1) % MaxSize —— 队尾指针后移,当移到最后一个后,下次移动会到第一个位置

队列长度:(Q.rear + MaxSize - Q.front) % MaxSize

三、栈的应用

栈在括号匹配中的应用

用栈实现括号匹配

  • ((())) 最后出现的左括号最先被匹配 (栈的特性—LIFO);
  • 遇到左括号就入栈;
  • 遇到右括号,就“消耗”一个左括号 (出栈);

匹配失败情况:

  • 扫描到右括号且栈空,则该右括号单身;
  • 扫描完所有括号后,栈非空,则该左括号单身;
  • 左右括号不匹配;
#define MaxSize 10   

typedef struct{
    char data[MaxSize];
    int top;
} SqStack;

//初始化栈
InitStack(SqStack &S)

//判断栈是否为空
bool StackEmpty(SqStack &S)

//新元素入栈
bool Push(SqStack &S, char x)

//栈顶元素出栈,用x返回
bool Pop(SqStack &S, char &x)



bool bracketCheck(char str[], int length){
    SqStack S;      //声明
    InitStack(S);   //初始化栈

    for(int i=0; i<length; i++){
        if(str[i] == '(' || str[i] == '[' || str[i] == '{'){
            Push(S, str[i]);       //扫描到左括号,入栈
        }else{
            if(StackEmpty(S))      //扫描到右括号,且当前栈空
                return false;      //匹配失败
            
            char topElem;          //存储栈顶元素
            Pop(S, topElem);       //栈顶元素出栈

            if(str[i] == ')' && topElem != '(' )
                return false;

            if(str[i] == ']' && topElem != '[' )
                return false;

            if(str[i] == '}' && topElem != '{' )
                return false;       
        }
    }

    StackEmpty(S);                //栈空说明匹配成功
}


 栈在表达式求值中的应用

  •  前缀表达式 (波兰表达式):运算符在两个操作数前面
  • 后缀表达式 (逆波兰表达式):运算符在两个操作数后面
  • 中缀表达式 (需要界限符):运算符在两个操作数中间

中缀表达式转后缀表达式-手算

  • 步骤1: 确定中缀表达式中各个运算符的运算顺序
  • 步骤2: 选择下一个运算符,按照[左操作数 右操作数 运算符]的方式组合成一个新的操作数
  • 步骤3: 如果还有运算符没被处理,继续步骤2

“左优先”原则: 只要左边的运算符能先计算,就优先算左边的 (保证运算顺序唯一);

中缀:A + B - C * D / E + F
       ①   ④   ②   ③   ⑤     
后缀:A B + C D * E / - F +

中缀表达式转后缀表达式-机算

初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。从左到右处理各个元素,直到末尾。可能遇到三种情况:

  • 遇到操作数: 直接加入后缀表达式。
  • 遇到界限符: 遇到 ‘(’ 直接入栈; 遇到 ‘)’ 则依次弹出栈内运算符并加入后缀表达式,直到弹出 ‘(’ 为止。注意: '(' 不加入后缀表达式。
  • 遇到运算符: 依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到 ‘(’ 或栈空则停止。之后再把当前运算符入栈。

按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。

后缀表达式的计算—手算:
从左往右扫描,每遇到一个运算符,就让运算符前面最近的两个操作数执行对应的运算,合体为一个操作数

后缀表达式的计算—机算
用栈实现后缀表达式的计算(栈用来存放当前暂时不能确定运算次序的操作数)

  • 步骤1: 从左往后扫描下一个元素,直到处理完所有元素;
  • 步骤2: 若扫描到操作数,则压入栈,并回到步骤1;否则执行步骤3;
  • 步骤3: 若扫描到运算符,则弹出两个栈顶元素,执行相应的运算,运算结果压回栈顶,回到步骤1;

栈在递归中的应用

函数调用的特点:最后被调用的函数最先执行结束(LIFO)

函数调用时,需要用一个栈存储:

  • 调用返回地址
  • 实参
  • 局部变量

递归调用时,函数调用栈称为 “递归工作栈”:

  • 每进入一层递归,就将递归调用所需信息压入栈顶;
  • 每退出一层递归,就从栈顶弹出相应信息;

缺点:太多层递归可能回导致栈溢出;

适合用“递归”算法解决:可以把原始问题转换为属性相同,但规模较小的问题
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/197454.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大数据看全国疫情生活,北京、武汉已过疫情拐点

自2022年11月末&#xff0c;很多地区新冠病例数据呈现下降趋势&#xff0c;与实际感知有明显差异。2022年12月14日&#xff0c;国家疾控中心发布消息&#xff0c;核酸检测实行“愿检尽检”&#xff0c;不再公布无症状感染者数据。因此&#xff0c;网友们想要了解所在地和老家的…

python能做的100件事04 - python解析PDF

1. python常用pdf库 名称特点PyPDF2已不再维护&#xff0c;继任者PyPDF4 ,但很长时间没有更新了,能读不能写pdfrw能读不能写&#xff0c;但可以兼容ReportLab写ReportLab商业版的开源版本&#xff0c;能写不能读pikepdf能读不能写pdfplumber能读不能写PyMuPDF读写均可,基于GPL…

Vue面试题2

1&#xff1a;vue.js的两个核心是什么&#xff1f; 答:数据驱动和组件化。 2&#xff1a;vue生命周期钩子函数有哪些&#xff1f; 答:总共分为8个阶段创建前/后&#xff0c;载入前/后&#xff0c;更新前/后&#xff0c;销毁前/后 3&#xff1a;第一次页面加载会触发哪几个钩子…

C语言二维数组中:主次对角线求和,上下三角求和,杨辉三角,矩阵转置

p8 有些的结论需要直接记住 目录 矩阵转置 主对角线和次对角线 下三角 和上三角&#xff08;一般是让求和&#xff09; 下三角 上三角 杨辉三角 矩阵转置 不是方阵 需要用到第二个二维数组 b[i][j]a[i][j] 是方阵 方法1 借助第二个二维数组&#xff0c;同上 方…

C++基础-3函数

一、函数 1.概述 作用&#xff1a;将一段经常使用的代码封装起来&#xff0c;减少重复代码 而一个较大的程序&#xff0c;一般分为若干个代码块&#xff0c;每个模块实现特定的功能 2.定义 5个内容&#xff1a; ①返回值类型 ②函数名 ③参数列表 ④函…

力扣刷题记录——1287. 有序数组中出现次数超过25%的元素、1299. 将每个元素替换为右侧最大元素 、1413. 逐步求和得到正数的最小值

本专栏主要记录力扣的刷题记录&#xff0c;备战蓝桥杯&#xff0c;供复盘和优化算法使用&#xff0c;也希望给大家带来帮助&#xff0c;博主是算法小白&#xff0c;希望各位大佬不要见笑&#xff0c;今天要分享的是——《力扣刷题记录——1287. 有序数组中出现次数超过25%的元素…

【vector的模拟实现】

目录 1 类的成员变量 2 常用成员函数和迭代器 3 增删查改 3.1 reverse 3.2 push_back 3.3 resize 3.4 insert && erase 4 默认成员函数 4.1 构造函数 4.2 拷贝构造 4.3 赋值运算符重载 4.4 析构函数 前面我们详细介绍了string类的使用&#xff0c;vector的…

关于 JSON 数据格式的完全使用指南

前言 本文将对 JSON 的语法、解析、序列化进行详细的说明&#xff0c;帮助大家掌握 JSON 的使用方式。 如果文中有不对、疑惑或者错字的地方&#xff0c;欢迎在评论区留言指正&#x1f33b; 一、JSON简介 在 JSON 之前&#xff0c;XML 曾经一度成为互联网上传输数据的事实标…

【论文阅读】【剪枝】Learning Efficient Convolutional Networks through Network Slimming

摘要 深度卷积神经网络&#xff08;CNN&#xff09;在许多实际应用中的部署在很大程度上受到其高计算成本的阻碍。在本文中&#xff0c;我们提出了一种新的神经网络学习方案&#xff0c;以同时1&#xff09;减小模型大小&#xff1b;2&#xff09; 减少运行时内存占用&…

Kali Linux- 社会工程及压力工具教程

在本章中&#xff0c;我们将了解 Kali Linux 中使用的社会工程工具。 文章目录社会工程Kali Linux - 压力工具SlowhttptestinvitefloodTHC-SSL-DOS总结社会工程 社会工程师工具包 &#xff08;SET&#xff09; 是一个专为社会工程设计的开源渗透测试框架。SET具有许多自定义攻…

第九章(12):STL之常用查找算法

文章目录前情回顾常用查找算法findfind_ifadjacent_findbinary_searchcountcount_if下一座石碑&#x1f389;welcome&#x1f389; ✒️博主介绍&#xff1a;一名大一的智能制造专业学生&#xff0c;在学习C/C的路上会越走越远&#xff0c;后面不定期更新有关C/C语法&#xff0…

读懂用户之用户调研怎么做?(内附模板教程)

随着互联网的发展&#xff0c;不管是做产品设计、运营&#xff0c;还是市场推广&#xff0c;我们都需要思考的是“用户真正想要的是什么&#xff1f;”。这时候&#xff0c;用户调研的重要性就凸显出来了。 一、什么是用户调研 用户调研&#xff0c;指通过各种方式得到受访者的…

Redis常用指令

3. 常用指令 在这部分中呢&#xff0c;我们家学习两个知识&#xff0c;第一个是key的常用指令&#xff0c;第二个是数据库的常用指令。和前面我们学数据类型做一下区分&#xff0c;前面你学的那些指令呢&#xff0c;都是针对某一个数据类型操作的&#xff0c;现在学的都是对所…

#14环形链表#

环形链表 1题目链接 链接 2思路 slow和fast指向链表的开始 slow一次走一步 fast一次走两步 不带环 fast就会为空 带环 fast就会在环里追上slow 3实现 bool hasCycle(struct ListNode* head) {struct ListNode* slow head, * fast head;while (fast && fast->ne…

微信小程序学习第3天——网络数据请求

一、小程序网络请求限制 1、必须https类型的接口 2、必须将接口的域名添加到信任列表中 二、配置request合法域名 配置步骤&#xff1a;登录微信小程序管理后台 -> 开发 -> 开发设置 -> 服务器域名 -> 修改 request 合法域名 点击修改request合法域名&#xf…

【自动化测试】从0开始玩转docker—— 01软件安装

目的 CI / CD在目前各类互联网企业中已然成为推动软件开发行为的重要基础设施服务。同样的对于测试团队来说更是有着举足轻重的重大意义&#xff0c;无论是测试左移的具象化提现亦或是持续测试的顺利开展&#xff0c;掌握这一技能已是广大软件测试工程师的必修课。分享这一技术…

Springboot+Vue+Uniapp自媒体视频系统

简介&#xff1a;本项目采用了基本的springbootvueuniapp设计的自媒体系统。详情请看主要截图。经测试&#xff0c;本项目正常运行。本项目适用于Java毕业设计、课程设计学习参考等用途。 项目描述 项目名称SpringbootVueUniapp自媒体视频系统源码作者LHL项目类型Java EE项目 …

C++vector容器

目录 1.vector基本概念 2.构造函数 3.vector赋值操作&#xff0c;&#xff0c;assign 4.vector容量和大小 ,empty,capacity,size,resize 5.vector的插入和删除&#xff0c;push_back,pop_back,insert,erase,clear 6.vector数据存取&#xff0c;at,[],遍历 7.vector互换…

C++之引用(上)

文章目录前言一、引用二、引用的写法三、引用特性1.引用在定义时必须初始化2. 一个变量可以有多个引用3. 引用一旦引用一个实体&#xff0c;再不能引用其他实体三、引用的权限&#xff08;含例子&#xff09;四、常引用总结前言 今天要介绍的是C中的一个新概念——引用。 一、…

MobaXterm使用指南

MobaXterm使用指南 1. 介绍 通俗的来讲&#xff0c;MobaXterm就是一款SSH客户端&#xff0c;它帮助我们在Windows操作系统下去连接并操作Linux服务器。MobaXterm 又名 MobaXVT&#xff0c;是一款增强型终端、X 服务器和 Unix 命令集(GNU/ Cygwin)工具箱。MobaXterm 可以开启多…