详解数据结构:栈

news2024/11/19 17:35:50

一、顺序栈

顺序栈的存储方式如下:

从图中可以看出,顺序栈需要两个指针,base指向栈底,top指向栈顶。

typedef struct SqStack {

    ElemType *base; //栈底指针

    ElemType *top; //栈顶指针

}SqStack;

说明:

ElemType是元素类型,需要什么类型就写什么类型。

typedef将结构体等价于类型名Sqstack。

栈定义好了之后,还要先定义一个最大的分配空间,这和数组一样,需要预先分配空间,因此可以采用宏定义:

#define Maxsize 100;  //预先分配空间,这个数值根据实际需要预估确定

上面的结构体定义采用了动态分配的形式,也可以采用静态分配的形式,使用一个长数组存储数据元素,一个整型下标记录栈顶元素的位置。静态分配的顺序栈结构定义如下:

typedef struct SqStack {

    ElemType data[Maxsize]; //定长数组

    int top; //栈顶下标

}SqStack;

注意:栈只能在一端操作,后进先出,是人为规定的,也就是说不允许在中间查找,取值,插入,删除等操作。顺序栈本身是顺序存储的,有人就想:我偏要从中间取一个元素,这也是可以的,但这样做,这就不是栈了。

1、顺序栈的初始化

初始化一个空栈,动态分配Maxsize大小的空间,用S.top和S.base指向该空间的基地址。

代码实现:

bool InitStack(SqStack &S) //构造一个空栈S

{

    S.base=new int[Maxsize];//为顺序栈分配一个最大容量为Maxsize的空间

    if(!S.base)    //空间分配失败

        return false;

    S.top=S.base;  //top初始为base,空栈

    return true;

}

2、入栈

入栈前要判断是否栈满,如果栈已满,则入栈失败;否则将元素放入栈顶,栈顶指针向上移动一个位置(top++)。

图解:

输入1,入栈(左图)

接着输入2,入栈(右图)

代码实现:

bool Push(SqStack &S,int e) // 插入元素e为新的栈顶元素

{

    if(S.top-S.base==Maxsize) //栈满

        return false;

   *(S.top++)=e; //元素e压入栈顶,然后栈顶指针加1,等价于*S.top=e; S.top++;

    return true;

}

3、出栈

出栈前要先判断是否为空栈,如果栈是空的,则出栈失败;否则将栈顶元素暂存给一个变量,栈顶指针向下移动一个位置(top--)。

完美图解:

栈顶元素所在的位置是S.top-1,因此把该元素取出来,暂存在变量e中,然后S.top指针向下移动一个位置。因此可以先移动一个位置,即--S.top,然后再取出元素。

例:栈顶元素4出栈前后的状态

注意:因为顺序栈删除一个元素时,并没有销毁该空间,所以4其实还在那个位置,只不过下一次再有元素进栈时,就把他覆盖了。相当于该元素已出栈,因为栈的内容时S.base到S.top-1。

代码实现:

bool Pop(SqStack &S,int &e) //删除S的栈顶元素,暂存在变量e中

{

    if(S.base==S.top) //栈空

        return false;

    e=*(--S.top); //栈顶指针减1,将栈顶元素赋给e

    return true;

}

4、取栈顶元素

取栈顶元素和出栈不同。取栈顶元素只是把栈顶元素复制一份,栈顶指针未移动,栈内元素个数未变。而出栈时栈顶指针向下移动一个位置,栈内不在包含这个元素。

完美图解:

取栈顶元素*(S.top-1)即元素4,取值后S.top指针没有改变,栈内元素的个数也没有改变。

代码实现:

int GetTop(SqStack S) //返回S的栈顶元素,栈顶指针不变

{

    if(S.top!=S.base)  //栈非空

        return *(S.top-1); //返回栈顶元素的值,栈顶指针不变

    else

        return -1;

}

二、链栈

栈可以顺序存储,也可以用链式存储。

顺序栈是分配一段连续的空间,需要两个指针:base指向栈底,top指向栈顶,而链栈每个节点的地址都不连续,只需要一个栈顶指针即可。

链栈的每个节点都包含两个域:数据域和指针域。可以把链栈看作一个不带头结点的单链表,但只能在头部进行插入、删除、取值等操作,不可以在中间和尾部操作。

链栈的结构体定义如下:

typedef struct Snode{

    ElemType data; //数据域

    struct Snode *next; //指针域,指向下一个节点的指针

}Snode,*LinkStack;

链栈的节点定义和单链表一样,只不过它只能在栈顶那一端操作而已。

1、链栈的初始化

初始化一个空的链栈是不需要头结点的,因此只需要让栈顶指针为空即可。

代码实现:

typedef struct Snode{

    ElemType data; //数据域

    struct Snode *next; //指针域,指向下一个节点的指针

}Snode,*LinkStack;

2.入栈

入栈是将新元素节点压入栈顶。因为链栈中第一个节点为栈顶,因此将新元素节点插入到第一个节点的前面,然后修改栈顶指针指向新结点即可。有点像堆柴,将新的节点堆在栈顶之上,新的节点成为新的栈顶。

完美图解:

(1)生成新结点。入栈前要创建一个新结点,将元素e存入该结点的数据域。

代码实现:

p=new Snode; //生成新结点,用p指针指向该结点

p->data=e; //将e放在新结点数据域

(2)将新元素节点插入到第一个节点的前面,然后修改栈顶指针指向新结点。

赋值解释:

  • p->next=S; //将新结点的指针域指向S,即将S的地址赋值给新结点的指针域。
  • S=p;    //修改栈顶指针为p

整体代码实现:

bool Push(LinkStack &S,int e) //在栈顶插入元素e

{

    LinkStack p;

    p=new Snode; //生成新结点

    p->data=e; //将e放在新结点数据域

    p->next=S; //将新结点的指针域指向S,即将S的地址赋值给新结点的指针域

    S=p;    //修改栈顶指针为p

    return true;

}

3.出栈

出栈就是把栈顶元素删除,绕过指针指向下一个节点,然后释放该结点空间。

赋值解释:

  • p=S;  //用p保存栈顶元素地址,以备释放
  • S=S->next;  //修改栈顶指针,指向下一个结点
  • delete p;  //释放原栈顶元素的空间

代码实现:

bool Pop(LinkStack &S,int &e) //删除S的栈顶元素,用e保存其值

{

    LinkStack p;

    if(S==NULL) //栈空

        return false;

    e=S->data;  //将栈顶元素赋给e

    p=S;  //用p保存栈顶元素地址,以备释放

    S=S->next;  //修改栈顶指针,指向下一个结点

    delete p;  //释放原栈顶元素的空间

    return true;

}

4.取栈顶元素

取栈顶元素和出栈不同,取栈顶元素只是把栈顶元素赋值一份,栈顶指针并没有改变。而出栈是指删除栈顶元素,栈顶指针指向了下一个元素。

代码实现:

int GetTop(LinkStack S) //返回S的栈顶元素,不修改栈顶指针

{

    if(S!=NULL) //栈非空

        return S->data; //返回栈顶元素的值,栈顶指针不变

    else

        return -1;

}

顺序栈和链栈的所以基本操作都只需要常数事件,所以在事件效率上难分伯仲。在空间效率方面,顺序栈需要预先分配固定长度的空间,有可能造成空间浪费或溢出;链栈每次只分配一个节点,除非没有内存,否则不会出现溢出,但是每个节点需要一个指针域,结构性开销增加。因此,如果元素个数变化较大,可以采用栈链;反之,可以采用顺序栈。在实际中,顺序栈比链栈应用更广泛。

配套完整代码(顺序栈):

#include<iostream>
using namespace std;

#define Maxsize 100  //预先分配空间,这个数值根据实际需要预估确定;

typedef struct SqStack {
	int *base; //栈底指针
	int *top; //栈顶指针
}SqStack;

bool InitStack(SqStack &S) //构造一个空栈S
{
	S.base=new int[Maxsize];//为顺序栈分配一个最大容量为Maxsize的空间
	if(!S.base)    //空间分配失败
		return false;
	S.top=S.base;  //top初始为base,空栈
	return true;
}

bool Push(SqStack &S,int e) // 插入元素e为新的栈顶元素
{
	if(S.top-S.base==Maxsize) //栈满
		return false;
	*(S.top++)=e; //元素e压入栈顶,然后栈顶指针加1,等价于*S.top=e; S.top++;
	return true;
}

bool Pop(SqStack &S,int &e) //删除S的栈顶元素,暂存在变量e中
{
	if(S.base==S.top) //栈空
		return false;
	e=*(--S.top); //栈顶指针减1,将栈顶元素赋给e
	return true;
}

int GetTop(SqStack S) //返回S的栈顶元素,栈顶指针不变
{
	if(S.top!=S.base)  //栈非空
		return *(S.top-1); //返回栈顶元素的值,栈顶指针不变
    else
        return -1;
}

int main()
{
	int n,x;
	SqStack S;
	InitStack(S);//初始化一个顺序栈S
	cout<<"请输入元素个数n:"<<endl;
	cin>>n;
	cout<<"请依次输入n个元素,依次入栈:"<<endl;
	while(n--)
    {
		cin>>x; //输入元素
		Push(S,x);
	}
	cout<<"元素依次出栈:"<<endl;
	while(S.top!=S.base)//如果栈不空,则依次出栈
    {
        cout<<GetTop(S)<<"\t";//输出栈顶元素
        Pop(S,x);   //栈顶元素出栈
    }
	return 0;
}

配套完整源代码(链栈):

#include<iostream>
using namespace std;

typedef struct Snode{
	int data; //数据域
	struct Snode *next; //指针域
}Snode,*LinkStack;

bool InitStack(LinkStack &S)//构造一个空栈S
{
	S=NULL;
	return true;
}

bool Push(LinkStack &S,int e) //在栈顶插入元素e
{
	LinkStack p;
	p=new Snode; //生成新结点
	p->data=e; //将e放在新结点数据域
	p->next=S; //将新结点的指针域指向S,即将S的地址赋值给新结点的指针域
	S=p;    //修改栈顶指针为p
	return true;
}

bool Pop(LinkStack &S,int &e) //删除S的栈顶元素,用e保存其值
{
	LinkStack p;
	if(S==NULL) //栈空
		return false;
	e=S->data;  //将栈顶元素赋给e
	p=S;  //用p保存栈顶元素地址,以备释放
	S=S->next;  //修改栈顶指针,指向下一个结点
	delete p;  //释放原栈顶元素的空间
	return true;
}

int GetTop(LinkStack S) //返回S的栈顶元素,不修改栈顶指针
{
	if(S!=NULL) //栈非空
		return S->data; //返回栈顶元素的值,栈顶指针不变
    else
        return -1;
}

int main()
{
	int n,x;
	LinkStack S;
	InitStack(S);//初始化一个顺序栈S
	cout<<"请输入元素个数n:"<<endl;
	cin>>n;
	cout<<"请依次输入n个元素,依次入栈:"<<endl;
	while(n--)
    {
		cin>>x; //输入元素
		Push(S,x);
	}
	cout<<"元素依次出栈:"<<endl;
	while(S!=NULL)//如果栈不空,则依次出栈
    {
        cout<<GetTop(S)<<"\t";//输出栈顶元素
        Pop(S,x);   //栈顶元素出栈
    }
	return 0;
}

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

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

相关文章

Meta:OpenAI不open?那我来?

Meta的Llama 3&#xff1a;开启开源AI新纪元&#xff0c;4000亿参数的科技奇迹 在人工智能的璀璨星河中&#xff0c;Meta公司以其最新的开源大模型Llama 3&#xff0c;再次引领科技潮流&#xff0c;为开源社区带来前所未有的活力与创新。这不仅是技术的一次飞跃&#xff0c;更…

岩石变角剪切试验夹具 技术参数

岩石变角试验夹具是根据TB10115-2014铁路工程岩石试验规程等标准利用压力机施加垂直荷载,通过一套特制的夹具使试件沿某一剪切面产生剪切破坏,然后通过静力平衡条件解析剪切面上的法向压应力和剪应力,从而绘制法向压应力&#xff08;σ&#xff09;与剪应力&#xff08;τ&…

Linux文件的特殊权限(SUID|SGID|SBIT)

一、SUID 介绍&#xff1a;SUID是一种对二进制程序进行设置的特殊权限&#xff0c;能够让二进制程序的执行者临时拥有所有者的权限&#xff08;仅对拥有执行权限的二进制程序有效&#xff09;。 &#xff08;一&#xff09;语法格式 chmod us 文件名&#xff08;设置SUID权限…

VsCode配置SSH连接远程服务器(手把手,学不会打我)

闲言少叙&#xff0c;直接上步骤。 1.打开VsCode安装插件 安装完成后可以看到左边多了一个图标Remote Explorer&#xff0c;如下图所示 2.添加服务器连接配置 点击该选项卡会进入SSH TARGETS的添加&#xff0c;如下图&#xff1a; 在点击红框框住的加号&#xff0c;会让你输…

鸿蒙系列--第一个程序HelloWorld

一、下载安装 下载DevEco Studio&#xff08;https://developer.huawei.com/consumer/cn/deveco-studio/&#xff09;并安装 二、创建第一个鸿蒙应用 Next Compile SDK&#xff1a;鸿蒙SDK版本 Mode&#xff1a;ArkTS的声明式开发范式与类Web 开发范式&#xff0c;官方主推S…

vLLM-prefix浅析(System Prompt,大模型推理加速)

原文&#xff1a;vLLM-prefix浅析&#xff08;System Prompt&#xff0c;大模型推理加速&#xff09; 简介 本文浅析了在大模型推理加速方面一个非常优秀的项目 vLLM 的一个新特性 Prefix。在 Prompt 中有相同前缀时可以提高吞吐量降低延迟&#xff0c;换句话说可以省去这部分…

哈希函数、哈希表、布隆过滤器、一致性哈希

哈希函数 工程的哈希函数展示 哈希值根据余数分组的均匀性 哈希表原理 一致性哈希 虚拟节点技术

C++ 类对象

C是一种功能强大的编程语言&#xff0c;它拥有丰富的特性集合&#xff0c;使得我们可以编写出高效、可维护且性能卓越的代码。其高级概念包括运算符重载、静态成员、友元函数、匿名对象和嵌套类。这些概念在面向对象编程中扮演着至关重要的角色&#xff0c;它们提供了对对象行为…

Maxwell安装使用和简单案例

一、解压 cd /opt/software/ ​ tar -zxvf maxwell-1.29.2.tar.gz -C /opt/module/ ​ cd /opt/module/ 二、MySQL 环境准备 1、修改 mysql 的配置文件 修改 mysql 的配置文件&#xff0c;开启 MySQL Binlog 设置 vi /etc/my.cnf 添加以下内容 server_id1 log-binmysql-…

一篇文章带你掌握⽹络原理--HTTP/HTTPS(HTTP各个部分详解和HTTPS加密详解)

HTTP HTTP是什么? HTTP(全称为"超⽂本传输协议")是⼀种应⽤⾮常⼴泛的应⽤层协议. HTTP诞⽣与1991年.⽬前已经发展为最主流使⽤的⼀种应⽤层协议. 最新的HTTP3版本也正在完善中,⽬前Google/Facebook等公司的产品已经⽀持了. HTTP往往是基于传输层的TCP协议实现的…

软件测试方法汇总

种类繁多&#xff0c;记忆起来混乱&#xff0c; 如果把软件测试方法进行分类, 就会清晰很多。 我参考一些书籍和网上的资料&#xff0c; 把常用的软件测试方法列出来&#xff0c; 让大家对软件测试行业有个总体的看法。 从测试设计方法分类 总结&#xff1a; 实际工作中&…

29. 【Android教程】折叠列表 ExpandableListView

本节学习一个可折叠的 ListView&#xff0c;可以用在一些需要分类的场景下。通过 ExpandableListView 我们可以首先在 ListView 上展示大的分类&#xff0c;当点击某个类别的时候再将 ListView 做一个展开&#xff0c;展示该类下的所有子类供用户选择。它与 ListView 的不同主要…

css样式,经典老问题回顾

跑到了一堆来了 这时需要在父div加相对定位的css 传说中的 子绝父相

Python Flask Web框架快速入门

Flask 入门Demo Flask 开发环境搭建&#xff0c;执行如下指令&#xff1a; pip install flask # 第一节: Flask 快速入门from flask import Flask app Flask(__name__)app.route(/flask) def hello_flask():return Hello Flaskapp.run() 核心代码剖析&#xff1a; 从 fla…

微信小程序开发之多图片上传+.NET WebAPI后端服务保存图片资源

前言&#xff1a; 最近开发的一个微信小程序项目需要做一个同时选中三张&#xff08;或者是多张&#xff09;图片一起上传到服务端&#xff0c;服务端保存图片资源并保存的功能。发现在微信小程序开发中会有很多场景会使用到多图片上传并保存到的功能&#xff0c;所以我把自己总…

酷开科技抓住“客厅经济”发展的机遇,不断对酷开系统升级赋能

酷开科技抓住“客厅经济”发展的机遇&#xff0c;不断对酷开系统升级赋能&#xff0c;打造新的生活场景&#xff0c;满足消费者的不同生活需求&#xff0c;酷开科技的产品和服务让消费者能够在家庭空间中享受到更加智能、便捷和温馨的时光。同样凭借更加包容、开放的生态体验&a…

unordered_map 与map使用说明

目录 背景&#xff1a; 问题分析&#xff1a; 使用APE工具&#xff0c;查看录制的原始门信号是否存在异常 查看报文读取是否存在问题 分析报文读取代码 为什么在Windows系统中&#xff0c;解析后的门信号没有存在跳变情况 是否Windows 和Linux下 unordered_map中数据先后…

文本批量高效编辑管理,支持将文本进行自定义行数进行拆分,实现文本的高效管理

在信息爆炸的时代&#xff0c;文本文件的管理和编辑成为了许多工作和学习中不可或缺的一部分。面对大量的文本内容&#xff0c;如何高效地进行编辑和管理成为了一个挑战。现在&#xff0c;我们为您带来了一款强大的批量文本编辑管理工具&#xff0c;支持自定义行数拆分&#xf…

面试题集中营—分布式共识算法

分布式共识算法目标 分布式主要就是为了解决单点故障。一开始只有一个服务节点提供服务&#xff0c;如下图所示。那么如果服务节点挂了&#xff0c;对不起等着吧。 为了服务的高可用性&#xff0c;我们一般都会多引入几个副节点当备份&#xff0c;当服务节点挂了&#xff0c;就…

BRC20铭文铭刻解析

BRC20铭文铭刻的出现对于智能制造无疑是一个重要的里程碑。随着科技的飞速发展&#xff0c;智能制造已经成为制造业发展的必然趋势&#xff01;智能制造是指通过运用人工智能、物联网、大数据等先进技术&#xff0c;实现生产过程的自动化、智能化和高效化。 1. BRC20铭文的概念…