数据结构栈和队列

news2025/4/11 13:48:55

3.栈和队列

3.1栈和队列的定义和特点

  • 栈和队列是两种常用的、重要的数据结构
  • 栈和队列是限定插入和删除只能在表的 “ 端点 ”进行的线性表
  • 栈和队列是线性表的子集(是插入和删除位置受限的线性表)

栈的应用:

​ 由于栈的操作具有后进先出的固有特性,使得栈成为程序设计中的有用工具。另外,如果问题求解的过程具有“后进先出”的天然特性的话,则求解的算法中也必然需要使用“栈”。如(数制转换、表达式求值、括号匹配的检验、八皇后问题、行编辑程序、函数调用、迷宫求解、递归调用的实现)

队列的应用:

​ 由于队列的操作具有先进先出的特性,使得队列成为程序设计中解决类似排队问题的有用工具。

3.1.1栈的定义和特点

  • 栈是一个特殊的线性表,是限定仅在一端(通常是表尾)进行插入和删除操作的线性表。
  • 又称为后进先出的线性表,简称LIFO结构
  • 存储结构,用顺序栈或链栈存储均可,但以顺序栈更常见。

栈的相关概念

​ 栈是仅在表尾进行插入、删除操作的线性表。

​ 表尾(即an端)称为栈顶Top;表头(即a1端)称为栈底Base

​ 插入元素到栈顶(即表尾)的操作,称为入栈(压栈 PUSH)。

​ 从栈顶(即表尾)删除最后一个元素的操作,称为出栈(POP)。

3.1.2队列的定义和特点

  • 队列是一种先进先出(FIFO)的线性表。在表一端插入(表尾),在另一端(表头)删除。(头删尾插)
  • 存储结构:顺序对或链队,以循环顺序队列更常见。

3.2案例引入

1、进制转换

十进制整数N向其他进制数d(二、八、十六)的转换是计算机实现计算的基本问题。

转换法则:除以d倒取余 n=(n div d)*d + n mod d 其中div为整除运算,mod为求余运算。

例 把十进制数159转换为八进制数

(159)10=(237)8

2、括号匹配的检验

假设表达式中允许包含两种括号:圆括号和方括号

其嵌套的顺序随意,即

  1. ([ ] ())或[([ ] [ ])]为正确格式
  2. [( ] )] 为错误格式
  3. ([ ())或(()] )为错误格式

3.3栈的表示和操作的实现

3.3.1顺序栈的表示和实现

存储方式:同一般线性表的顺序存储结构完全相同,利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端。

  • 附设top指针,指示栈顶元素在顺序栈中的位置。
  • 另设base指针,指示栈底元素在顺序栈中的位置。

但是,为了方便操作,通常top指示真正的栈顶元素之上的下标地址。

  • 另外,用stacksize表示栈可使用的最大容量。

空栈:base == top 是栈空标志

栈满:top - base == stacksize

栈满时的处理方法:

  1. 报错,返回操作系统。
  2. 分配更大的空间,作为栈的存储空间,将原栈的内容移入新栈。

使用数组作为顺序栈存储方式的特点:简单、方便、但易产生溢出(数组大小固定)

  • 上溢(overflow):栈已经满,又要压入元素。
  • 下溢(underflow):栈已经空,还要弹出元素。

顺序栈的表示

#define MAXSIZE 100
typedef struct {
	int* base;  //栈底指针
	int* top;  //栈顶指针
	int stacksize;  //栈可用最大容量
}SqStack;
1、顺序栈的初始化
//顺序栈的初始化
bool InitStack(SqStack& s) {//构造一个空栈
	s.base = new int[MAXSIZE];//或s.base=(int *)malloc(MAXSIZE*sizeof(int))
	if (!s.base) return false;//存储分配失败
	s.top = s.base;//栈顶指针等于栈底指针
	s.stacksize = MAXSIZE;
	return true;
}
2、顺序栈判断栈是否为空
//判断栈是否为空
bool StackEmpty(SqStack s) {
	if (s.top == s.base)return true;
	else return false;
}
3、求顺序栈长度
//求顺序栈长度
int StackLength(SqStack s) {
	return s.top - s.base;
}
4、清空顺序栈
//清空顺序栈
bool ClearStack(SqStack s) {
	if (s.base) s.top = s.base;
	return true;
}
5、销毁顺序栈
bool DestroyStack(SqStack& s) {
	if (s.base) {
		delete s.base;
		s.stacksize = 0;
		s.base = s.top = 0;
	}
	return true;
}
5、顺序栈的入栈

【算法步骤】

  1. 判断是否栈满,若满则出错(上溢)
  2. 元素e压入栈顶
  3. 栈顶指针加1
bool Push(SqStack& s, int e) {
	if (s.top - s.base == s.stacksize) return false;//栈满
	*s.top++ = e;//*s.top=e;  s.top++;
	return true;
}
6、顺序栈的出栈

【算法步骤】

  1. 判断是否栈空,若空则出错(下溢)
  2. 栈顶指针减1
  3. 获取栈顶元素e
bool Pop(SqStack& s, int& e) {
	if (s.top == s.base)//等价以if(StackEmpty(s))
		return false;
	e = *--s.top;// --s.top;  e=*s.top
	return true;
}

3.3.2链栈的表示和实现

链栈的表示:

  • 链栈是运算受限的单链表,只能在链表头部进行操作
typedef struct StackNode {
	int data;
	struct  StackNode* next;
}StackNode,*LinkStack;
LinkStack s;
  • 链表的头指针就是栈顶
  • 不需要头结点
  • 基本不存在栈满的情况
  • 空栈相当于头结点指向空
  • 插入和删除仅在栈顶处执行
1、链栈的初始化
int InitStack(LinkStack& s) {
	LinkStack S;
	S = NULL;
	return true;
}
2、判断链栈是否为空
bool StackEmpty(LinkStack s) {
	if (s == NULL) return true;
	else return false;
}
3、链栈的入栈
bool Push_L(LinkStack& s, int e) {
	LinkStack p = new StackNode;//生成新结点p
	p->data = e;//将新结点数据域置为e
	p->next = s;//进新结点插入栈顶
	s = p;//修改栈顶指针
	return true;
}
4、链栈的出栈
bool Pop_L(LinkStack& s, int& e) {
	if (s == NULL) return false;
	e = s->data;
	LinkStack p;
	p = s;
	s = s->next;
	delete p;
	return true;
}
5、取栈顶元素
int GetTop(LinkStack s) {
	if (s != NULL) return s->data;
}

3.4栈与递归

递归的定义:

  • 若一个对象部分地包含它自己,或用它自己给自己定义,则称这个对象是递归的;

  • 若一个过程直接地或间接地调用自己,则称这个过程是递归的过程。

    • 如:递归求n的阶乘

      long Fact(long n){
        if(n==0) return 1;
        else return n*Fact(n-1);
      }
      
    • 以下三种情况常常用到递归方法

      • 递归定义的数学函数
        • 阶乘函数
        • 2阶Fibonaci数列
      • 具有递归特性的数据结构
        • 二叉树
        • 广义表
      • 可递归求解的问题
        • 迷宫问题
        • Hanoi塔问题
  • 递归问题——用分治法求解

    分治法:对于一个较为复杂的问题,能够分解成几个相对简单的且解法相同或类似的子问题来求解。

    必备的三个条件

    1. 能将一个问题转变成一个新问题,而新问题与原问题的解法相同或类同,不同的仅是处理的对象,且这些处理对象是变化有规律的。
    2. 可以通过上述转化而使问题简化。
    3. 必须有一个明确的递归出口,或称递归的边界。

    分治法求解递归问题算法的一般形式:

    void p(参数表){
      if(递归结束条件) 可直接求解步骤;  -------基本项
      else p(较小的参数);    --------归纳项
    }
    
    long Fact(long n){
      if(n==0) return 1;   //基本项
      else return n*Fact(n-1);  //归纳项
    }
    
  • 函数调用过程

    调用前,系统完成:

    1. 实参,返回地址等传递给被调用函数
    2. 为被调用函数的局部变量分配存储区
    3. 将控制转移到被调用函数的入口

    调用后,系统完成:

    1. 保存被调用函数的计算结果
    2. 释放被调用函数的数据区
    3. 依照被调用函数保存的返回地址将控制转移到调用函数

    当多个函数构成嵌套调用时:遵循后调用的先返回 栈

  • 递归的优缺点

    优点:结构清晰,程序易读。

    缺点:每次调用要生成工作记录,保存状态信息,入栈;返回时要出栈,恢复状态信息。时间开销大。

  • 递归—>非递归

    方法1:尾递归、单向递归 -->循环结构

    方法2:自用栈模拟系统的运行时栈

3.5队列的表示和操作的实现

  • 相关术语(头删尾插)
    • 队列(Queue)是仅在表尾进行插入操作,在表头进行删除操作的线性表。
    • 表尾即an端,称为队尾;表头即a1端,称为队头。
    • 它是一种**先进先出(FIFO)**的线性表。
    • 插入元素称为入队,删除元素称为出队
    • 队列的存储结构为链队或顺序队(常用循环顺序队)。

3.5.1队列的顺序表示和实现

队列的顺序表示——用一维数组base[MAXQSIZE]

#define MAXSIZE 100
typedef struct {
	int* base;//初始化的动态分配存储空间
	int front;//头指针,若队列不空。指向队列头元素
	int rear;//尾指针,若队列不空,指向队列尾元素的下一个位置。
}SqQueue;

初始:front = rear = 0

入队:base [rear] = x ; rear++;

出队:x = base [ front] ; front++;

空队标志:front == rear

设数组大小为MAXSIZE,rear = MAXSIZE时,发出溢出

  • 若front = 0 ,rear=MAXSIZE时,再入队——真溢出
  • 若front ≠ 0 ,rear=MAXSIZE时,再入队——假溢出

c++ 循环队列基本操作案例_快乐的半半的博客-CSDN博客

解决假上溢的方法

  1. 将队中元素依次向队头方向移动。

    缺点:浪费时间。每移动一次,队中元素都要移动。

  2. 将队空间设想成一个循环的表,即分配给队列的m个存储单元可以循环使用,当rear为MAXSIZE时,若向量的开始端空着,又可从头使用空着的空间。当front为MAXSIZE时,也是一样。

解决假上溢的方法——引入循环队列

base[0]接在base[MAXSIZE-1]之后,若rear+1==M,则令rear=0;

实现方法:利用模(mod,C语言中:%)运算

插入元素:Q.base[Q.rear] = x;

​ Q.rear = (Q.rear + 1) % MAXSIZE;

删除元素:x = Q.base[s.front]

​ Q.front = (Q.front + 1) % MAXSIZE;

C/C++语言顺序队列:循环队列(插入,删除,遍历)_风扬Liron的博客-CSDN博客_循环队列插入

循环队列的相关计算公式_smile4lee的博客-CSDN博客

如何判断队空和队满?

解决方案:

  1. 另外设一个标志以区别队空、队满。
  2. 另设一个变量,记录元素个数。
  3. 少用一个元素空间

循环队列解决队满时判断方法——少用一个元素空间:

C/C++_循环队列(详解)_weifc-wei的博客-CSDN博客_循环队列

队空:front == rear

队满:(rear + 1)% MAXSIZE == front

1、队列的初始化
bool InitQueue(SqQueue& Q) {
	Q.base = new int[MAXSIZE];//分配数组空间
	//或Q.base=(int*)malloc(MAXSIZE*sizeof(int));
	if (!Q.base) return false;//存储分配失败
	Q.front = Q.rear = 0;//头指针尾指针置为0,队列为空
	return true;
}
2、求队列的长度
int QueueLength(SqQueue Q) {
	return ((Q.rear - Q.front+MAXSIZE)%MAXSIZE);
}
3、循环队列入队
bool EeQueue(SqQueue& Q, int e) {
	if ((Q.rear + 1) % MAXSIZE == Q.front) return false;//队满
	Q.base[Q.rear] = e;//新元素加入队尾
	Q.rear = (Q.rear + 1) % MAXSIZE;//队尾指针+1
	return true;
}
4、循环队列出队
bool DeQueue(SqQueue& Q, int& e) {
	if (Q.front == Q.rear) return false;//队空
	e = Q.base[Q.front];//保存队头元素
	Q.front = (Q.front + 1) % MAXSIZE;//队头指针+1
	return true;
}
5、取队头元素
int GetHead(SqQueue Q) {
	if (Q.front != Q.rear)//队列不为空
		return Q.base[Q.front];//返回队头指针元素的值,队头指针不变
}

3.5.2队列的链式表示和实现

若用户无法估计所用队列的长度,则宜采用链队列

【数据结构&算法】一:线性表_大隐隐于市Ww的博客-CSDN博客_线性表

链队列的类型定义

typedef struct Qnode {
	int data;
	struct Qnode* next;
}QNode,*QuenePtr;
typedef struct {
	QuenePtr front;//队头指针
	QuenePtr rear;//队尾指针
}LinkQueue;

链队列运算指针变化状况:

队列的链式表示和实现_sumi_的博客-CSDN博客

1、链队列初始化
bool InitQueue_L(LinkQueue& Q) {
	Q.front = Q.rear = (QuenePtr)malloc(sizeof(QNode));
	if (!Q.front) return false;
	Q.front->next = NULL;
	return true;
}
2、销毁链队列(补充)

算法思想:从队头结点开始,依次释放所有结点。

bool DestroyQueue(LinkQueue& Q) {
	QNode* p;
	while (Q.front) {
		p = Q.front->next;
		free(Q.front);
		Q.front = p;
    //或者Q.rear=Q.front->next; free(Q.front); Q.front=Q.rear;
	}
	return true;
}
3、将元素e入队
bool EnQueue_L(LinkQueue& Q, int e) {
	QNode* p;
	p = (QuenePtr)malloc(sizeof(QNode));
	if (!p) return false;
	p->data = e;
	p->next = NULL;
	Q.rear->next = p;
	Q.rear = p;
	return true;
}
4、链队列出队
bool DeQueue_L(LinkQueue& Q, int& e) {
	if (Q.front == Q.rear) return false;
	QNode* p;
	p = Q.front->next;
	e = p->data;
	Q.front->next = p->next;
	if (Q.rear == p)Q.rear = Q.front;
	delete p;
	return true;
}
5、求链队列的队头元素
bool GetHead(LinkQueue Q, int& e) {
	if (Q.front == Q.rear) return false;
	e = Q.front->next->data;
	return true;
}

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

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

相关文章

国内疫情地图和省级疫情地图

基础地图演示 from pyecharts.charts import Mapfrom pyecharts.options import VisualMapOpts map Map() data [ ("北京", 99), ("上海", 199), ("湖南", 299), ("台湾", 199), ("安徽", 299), ("广州", 399…

干货满满-运营校园跑腿小程序

校园跑腿是指在校园内提供代办、送餐、购物等服务的一种形式。学生可以通过跑腿服务解决一些日常生活中的繁琐事务,节省时间和精力。在校园跑腿小程序运营中,你可以尝试以下方法进行运营管理: (1)注册或加入相关的校园…

fdbus和proto编译

1. 下载protobuf和FDBUS 1.下载 FDBUS需要用到protobuf,所以需要提前安装好protobuf。 protobuf下载地址 https://gitee.com/it-monkey/protocolbuffers/ fdbus下载地址 https://gitee.com/jeremyczhen/fdbus 2. Windows编译 生成vs工程 打开CMake&#xff…

(vue)vue项目中引入外部字体

(vue)vue项目中引入外部字体 效果: 第一步 放置字体包,在assets下创建一个fonts文件夹,放入下载的字体文件 第二步 创建一个font.css文件用于定义这个字体包的名字 第三步 在App.vue的css中将这个css文件引入 第四步 页面使用 font-famil…

NumPy 专业人士应该掌握的 45 个技能

一、说明 NumPy(或Numeric Python)是每个数据科学和机器学习项目的核心。 整个数据驱动的生态系统在某种程度上依赖于NumPy及其核心功能。这使它成为 Python 有史以来最重要和改变游戏规则的库之一。 鉴于NumPy由于其无与伦比的潜力而在工业界和学术界具…

java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法

文章目录 一、记录文件相关操作方法二、代码1.读取路径返回List\<File>2.读取路径返回List\<String>3.删除文件夹4.删除文件 一、记录文件相关操作方法 二、代码 1.读取路径返回List<File> import org.slf4j.LoggerFactory; import org.slf4j.Logger;impo…

【学会动态规划】按摩师(11)

目录 动态规划怎么学&#xff1f; 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后&#xff1a; 动态规划怎么学&#xff1f; 学习一个算法没有捷径&#xff0c;更何况是学习动态规划&#xff0c; 跟我…

es通过rest接口_search、_delete_by_query查询与删除数据

1、rest接口查询数据 rest查询: http://localhost:9200/index_name/_search 查询表达式&#xff1a; {"query": {"wildcard": {"accountID": {"value": "v*"}}} }postman请求截图&#xff1a; 2、使用Rest接口删除数据 …

基于Lucene实现校园搜索引擎——太强搜索

完整资料进入【数字空间】查看——搜索"writebug" 实验环境 win10 一、实验内容 综合运用搜索引擎体系结构和核心算法方面的知识&#xff0c;基于开源资源搭建搜索引擎&#xff0c;具体包括如下几点&#xff1a; 抓取清华校园网内绝大部分资源&#xff0c;并且进行…

【NLP】温和解读:transformer的核心思想

变压器模型及其关键组件的概述。 一、介绍 在这篇博文中&#xff0c;我将讨论本世纪最具革命性的论文“注意力是你所需要的一切”&#xff08;Vaswani et al.&#xff09;。首先&#xff0c;我将介绍自我注意机制&#xff0c;然后介绍变形金刚的架构细节。在之前的博客文章《从…

【数据分析 最火 全集干货】Anaconda的安装及使用

关于我的专栏&#xff1a; 接下来会有许多关于“数据分析”的文章哦&#xff0c;记得看哦&#xff01;&#xff01;&#xff01; Python最详细最全面基础合集_adaptation_T_C的博客-CSDN博客 有兴趣&#xff0c;需要 的小伙伴可以免费订阅哦&#xff01;&#xff01;&#x…

阿里 P8 架构师 20 年经验!总结成微服务设计企业架构转型之道

前言 本文涉及两个方面的知识体系&#xff0c;即企业架构知识体系和软件架构知识体系。 企业架构和软件架构虽然都与 IT 相关&#xff0c;但其知识体系是完全不同的两个领域。一般而言&#xff0c;搞企业架构的人士不明白软件架构的细节和实现&#xff0c;而从事软件架构的架…

如何恢复损坏/删除的 Word 文件

有关如何修复不可读的 Microsoft Word 文件或 Rich Text 文件中的文本的分步说明。这些说明有助于从损坏的*.doc、*.docx、*.dot、*.dotx、*.rtf文件&#xff08;任何版本和大小&#xff09;中提取文本&#xff0c;只需单击几下&#xff1a; 从此处下载奇客数据恢复 &#xff…

React AntDesign写一个导出数据的提示语 上面有跳转的路径,或者点击知道了,关闭该弹层

效果如下&#xff1a; 代码如下&#xff1a; ForwardDataCenterModal(_blank);export const ForwardDataCenterModal (target?: string) > {let contentBefore React.createElement(span, null, 数据正在处理中&#xff0c;请稍后前往);let contentAfter React.creat…

【如何训练一个中译英翻译器】LSTM机器翻译模型训练与保存(二)

系列文章 【如何训练一个中译英翻译器】LSTM机器翻译seq2seq字符编码&#xff08;一&#xff09; 目录 系列文章1、加载训练集2、训练集数据处理3、网络搭建4、启动训练5、模型保存6、模型加载与推理 基于LSTM训练一个翻译器&#xff0c;要怎么做呢&#xff1f;其实很简单&am…

大厂案例 - 实时分析引擎

文章目录 概述内容收获思路建议 概述 网络安全态势越来越复杂&#xff0c;传统的基于单点的防护和攻击检测系统在应对现代网络攻击方面有着很大的局限性。 基于大数据平台&#xff0c;通过流式实时分析技术可以对全局网络空间进行实时的分析和异常检测&#xff0c;解决单点很…

2023年7月杭州/武汉/深圳制造业产品经理NPDP认证招生

产品经理国际资格认证NPDP是新产品开发方面的认证&#xff0c;集理论、方法与实践为一体的全方位的知识体系&#xff0c;为公司组织层级进行规划、决策、执行提供良好的方法体系支撑。 【认证机构】 产品开发与管理协会&#xff08;PDMA&#xff09;成立于1979年&#xff0c;是…

独立开发者必备的29个开源React后台管理模板

React Web应用程序开发管理后台可能非常耗时&#xff0c;这和设计所有前端页面一样重要。 以下是收集的近几年顶级React.js管理模板列表。 这些模板确实很有价值&#xff0c;使开发人员更容易构建应用程序后端的用户界面。 此外&#xff0c;它们将帮助您完善网站的管理后台&…

Clion开发STM32之W5500系列(综合实验)

说明 此为w5500模块的综合实验测试模块,包含dhcp、dns、ntp以上三个模块的驱动参考之前的文章&#xff0c;本篇不做说明.使用的开发芯片 stm32f103vet6系列,外设接口使用的spi2 实验内容: 通过dhcp动态获取ip,通过dns解析NTP服务域名的ip通过NTP服务ip获取时间 w5500配置驱…

【oracle数据库】单行函数

Oracle数据库中的单行函数 1.字符函数2.数值函数3.转换函数4.判空函数5.条件取值 1.字符函数 --length()求字符串长度 select length(aslkd) from dual --substr(,起始点,截取长度) 起始点从1开始 截取字符串 select substr(asd,2,2) from dual --concat(,)字符串拼接 sele…