数据结构之动态栈、静态栈详解

2023-11-12

程序都已经成功通过编译,运行无异常,如果你发现bug,请评论区留言,我们相互交流下经验。

定义:

栈是只能在一端进行数据的插入与删除的线性表。也可以理解为一种“先进后出”的存储结构。

按照内存生成的方式不同分为:

	静态栈 			动态栈

栈的几个概念:

  1. 允许进行插入、删除操作的一端称为栈顶。
  2. 表的另一端称为栈底。
  3. 当栈中没有数据元素时,称为空栈。
  4. 栈的插入操作通常称为进栈或入栈。
  5. 栈的删除操作通常称为退栈或出栈。

静态栈的代码如下:

1.静态栈 不用malloc函数

/*
	生成的内存为连续内存
	静态栈,当栈生成以后,长度已经定了 也就是一个数组,和一个栈顶元素(保存了,最后一个有效元素的位置)
	这种程序结构并不推荐
	top保存的为最后一个有效元素的地址
*/
# include <stdio.h>
# define Maxsize 10

/*
	定义一个结构体变量
	结构体变量有两部分1.是地址连续的数组内容 2.保存了最后那个元素的有效地址编号
*/

typedef struct Student
{
	int data[Maxsize];
	int top;  //top始终为当前元素的位置
}SqStack;

//函数申明
bool TravalList(SqStack * s);
int PopStack(SqStack * s,int &val);
bool PushStack(SqStack * s,int val);
void InitStack(SqStack * s);

int main()
{
	int val;
	SqStack sq; //当这条语句写下以后,栈已经生成  内存已进行分配 在主函数main中进行
				//生成一个结构体变量,操作系统并为之分配相应的内存,创建了一个数组+top的结构体,不用使用动态内存分配的malloc函数分配相应的内存
	sq.top=-1;  //设置栈顶元素为-1

	PushStack(&sq,1);  //压栈
	PushStack(&sq,2);
	PushStack(&sq,3);
	PushStack(&sq,4);
	PushStack(&sq,5);
	PushStack(&sq,6);  //压栈
	PushStack(&sq,7);
	PushStack(&sq,8);
	PushStack(&sq,9);
	PushStack(&sq,10);
	PushStack(&sq,11);
	TravalList(&sq);

	PopStack(&sq,val);
	printf("val = %d\n",val);

}

bool PushStack(SqStack * s,int val)
{
	if(s->top == Maxsize-1)
	{
		printf("栈满\n");
		return false;
	}
	else
	{
		s->top++;
		s->data[s->top]=val;
		return true;
	}
	
}

int PopStack(SqStack * s,int &val)//弹栈  出栈
{
	if(s->top == -1)
	{
		printf("栈空\n");
		return 0;
	}
	else
	{
		val = s->data[s->top];
		s->top--;
		return val;
	}
	
}

bool TravalList(SqStack * s)//遍历
{
	int i;
	if(s->top == -1)
	{
		printf("空栈\n");
		return false;
	}
	else
	{
		for(i=0;i<=s->top;i++)
		{
			printf("%d ",s->data[i]);
		}
		printf("\n");
		return true;
	}
}

重点:
上面的函数,在主函数中生成的栈,栈的长度已经固定 内存不是用动态分配函数进行分配,而是由主函数自动分配相应的内存。
2.静态栈 用malloc动态分配静态栈的地址程序:

/*
	用malloc生成的栈程序
	数组栈
	内存虽然用malloc动态生成,但是生成的内存大小固定,并不能进行扩展,程序较为死板

*/
# include <stdio.h>
# include <malloc.h>

# define Maxsize 10

typedef struct Student
{
	int data[Maxsize];
	int top;  //top始终为当前元素的位置
} SqStack;

bool TravalList(SqStack * &s);
int PopStack(SqStack * &s,int &val);
bool PushStack(SqStack * &s,int val);
void InitStack(SqStack * &s);
void DestroyStack(SqStack *&s);

bool GetTop(SqStack *s, int &e);
bool StackEmpty(SqStack *s);

int main()
{
	int val;
	/*
	SqStack sq; //当这条语句写下以后,栈已经生成
				//生成一个结构体变量,操作系统并为之分配相应的内存,创建了一个数组+top的结构体,不用使用动态内存分配的malloc函数分配相应的内存
	
	SqStack *Sq = &sq; //函数引用时候应该进行的操作,必须进行这样的操作

	上面程序虽然也能测试成功,但是会造成内存的二次分配造成内存很大的浪费,所以并不采用上面的程序生成数据
	严格来说有很大的bug

	*/

	SqStack * Sq;//该条程序完全避免了上面的尴尬 该程序只定义了一个指针变量sq,虽然也进行内存的分配,但是只是给指针分配内存 (不论指向什么数据类型都是四个字节) sq指向的数据类型为SqStack
					//该条程序也可以理解为定义一个头指针
	InitStack(Sq);// 将头指针指向头结点  头结点并不进行数据的赋值,指针的指向

	PushStack(Sq,1);  //压栈
	PushStack(Sq,2);
	TravalList(Sq);
	PopStack(Sq,val);
	printf("val = %d\n",val);

	DestroyStack(Sq);
	TravalList(Sq);
	//printf("%d\n",StackEmpty(Sq));
}

/*
	申请一块内存,供其使用 初始化栈内存 s指向一个一个结构体的首地址,并将s->top指向-1
*/
void InitStack(SqStack * &s)
{
	s = (SqStack *)malloc(sizeof(SqStack));
	s->top = -1;
}

/*
	进栈
*/
bool PushStack(SqStack * &s,int val)
{
	if(s->top == Maxsize-1)
	{
		printf("栈满\n");
		return false;
	}
	else
	{
		s->top++;
		s->data[s->top]=val;
		return true;
	}
	
}

/*
	出栈
*/
int PopStack(SqStack * &s,int &val)
{
	if(s->top == -1)
	{
		printf("栈空\n");
		return 0;
	}
	else
	{
		val = s->data[s->top];
		s->top--;
		return val;
	}
	
}

/*
	遍历栈内的所有元素
*/
bool TravalList(SqStack * &s)
{
	int i;
	if(s->top == -1)
	{
		printf("空栈\n");
		return false;
	}
	else
	{
		for(i=0;i<=s->top;i++)
		{
			printf("%d->",s->data[i]);
		}
		printf("NULL\n");
		return true;
	}
}

/*
	销毁栈元素,将申请的内存释放
*/
void  DestroyStack(SqStack *&s)
{
	free(s);
}

/*
	判断栈是否为空栈,返回值为bool值
*/
bool StackEmpty(SqStack *s)
{
	return(s->top==-1);
}

/*
	取栈顶元素,返回值为bool,但是可以通过函数参数,进行相应的调用输出
*/
bool GetTop(SqStack *s, int &e)
{	
	if (s->top == -1)	//栈为空的情况,即栈下溢出 	   
		return false;
	e=s->data[s->top];	//取栈顶指针元素的元素
		return true;
}

程序的标注解释较为清晰,不做过多解释。

动态栈:

特点:
定义了一个指向栈首成员和指向栈尾成员变量的结构体

# include <stdio.h>
# include <malloc.h>
# include <stdlib.h>

/*
	定义一个结构体变量,数据域 指针域

	可以看做一个自下向上生成的栈 栈顶在最上面 由top指向  在栈顶那进行进栈出栈
*/
typedef struct Student
{
	int data;
	struct Student * next;
}Node, * pNode;
/*
	定义一个结构体,有两个指针  分别指向 栈首  栈尾
*/
typedef struct stack
{
	pNode top;
	pNode bottom;
}Stack;


void ClearStack(Stack * s);
void TravelStack(Stack * s);
bool PopStack(Stack * s,int &val);
void InitStack(Stack * q);
void PushStack(Stack * s, int val);

int main(void)
{
	Stack  st;//该程序写下的用时,为st分配相应的内存 创建了一个结构体类型为Stack的结构体变量
	int val ;

	InitStack(&st);
	PushStack(&st,1);
	PushStack(&st,2);
	PushStack(&st,3);
	PushStack(&st,4);
	PushStack(&st,5);
	TravelStack(&st);
	PopStack(&st,val);
	printf("val = %d\n",val);
	TravelStack(&st);
	ClearStack(&st);
	TravelStack(&st);

	return 0;
}

//初始化栈  栈头指针 栈尾指针分别指向 生成的一块内存 并且 内存的指针域设置为空UNLL 即不指向
void InitStack(Stack * q)  // q为创建的Stack结构体变量(主函数创建并分配内存)
{
	q->top = (pNode)malloc(sizeof(Node));  //创建头结点 不保存数据  只为操作方便
	//为头结点分配相应的内存 并且q结构体变量的top成员(类型为Node * 指向Node类型的指针)变量指向该Node结构体变量
	if(q == NULL)
	{
		printf("内存分配失败\n");
		exit(-1);
	}
	else
	{
		q->bottom = q->top;//栈首栈尾指针分别指向相应的头结点
		q->top->next = NULL;
		printf("创建栈成功\n");
	}
}

// 压栈 生成一块新的结构体内存 该内存的指针域 指向上一块内存节点 数据域置为val  开始存储数据 并且指针的结构体的栈首指针指向新生成的地址
void PushStack(Stack * s, int val)
{
	pNode pNew = (pNode)malloc(sizeof(Node));//生成一块内存 类型为pNode类型 并让pNew指向该内存
	//分配一个Node类型大小的内存空间, 并把它赋给Node* 型的变量:pNew
	pNew->data = val;
	pNew->next = s->top;  //将s结构体变量的成员变量top指向的地址(也就是上一个节点的地址)赋值给新生成节点的指针域,即让新生成的节点指向上一个节点 
	s->top = pNew;  //s结构体的top成员变量,指向栈顶元素
}

// 出栈  释放空间
bool PopStack(Stack * s,int &val)
{
	if(s->top == s->bottom)
	{
		printf("空栈\n");
		return false;
	}
	else
	{
		/*
		val = s->top->data;
		free(s->top);
		s->top = s->top->next;
		return true;
		*/
		
		pNode r = s->top;//新生成类型为pNode类型的指针 该指针指向栈顶指针指向的节点 二者指向相同的节点
		val = r->data;  //数据
		s->top = r->next;  //栈顶元素的下一个元素
		free(r);
		r = NULL;
		return true;
		
	}
}

// 遍历数据
void TravelStack(Stack * s)
{

	if(s->top == s->bottom)
	{
		printf("栈顶指针 等于 栈底指针 空栈\n");
		return ;
	}
	else
	{
		pNode p;//对栈创建一个节点指针 指针指向栈顶元素
		p = s->top;  //s->top 即为栈顶结构体的地址 s->top->data  即为栈顶指针的成员变量data  相当于(*p).data p保存栈顶指针的地址 *p即 栈顶结构体
		while(p != s->bottom) //当p也就是栈顶指针不为栈底指针的时候 输出data成员 栈底不保存成员data元素 头结点
			{
				printf("%d->",p->data);
				p=p->next;
			}
		printf("NULL\n");
		return ;
	}
}

//清空链栈 但栈的头结点仍然存在 
void ClearStack(Stack * s)
{
	if(s->top == s->bottom)
	{
		printf("已空栈\n");
	}
	else
	{
		pNode  p = s->top;
		pNode q = NULL;
		while(p != s->bottom)
		{
			q = p->next;
			free(p);
			p = q;
		}
		s->top = s->bottom;
	}

}

特点:
巧妙采用头插法以及头指针和头结点进行操作的栈

/*
	该函数采用头插法进行压栈退栈
	创建了一个头指针(主函数中创建) 头结点(InitStack函数中创建),头指针指向头结点  只为操作方便s->next即为下一个节点,也就是栈顶

*/

# include <stdio.h>
# include <malloc.h>
# define ElemType int

typedef struct linknode
{     ElemType data;		//数据域
      struct linknode *next;	//指针域
}LinkStNode;


void TravelStack(LinkStNode *&s);
void DestroyStack(LinkStNode *&s);
void InitStack(LinkStNode *&s);
bool StackEmpty(LinkStNode *s);
void Push(LinkStNode *&s,ElemType e);
bool Pop(LinkStNode *&s,ElemType &e);
bool GetTop(LinkStNode *s,ElemType &e);

int main(void)
{
	int e = 0;
	/*
	LinkStNode s,*p;
	p = &s;
	*/

	LinkStNode *p;//创建一个指针变量 指向的结构体类型为LinkStNode  也就是链表创建头指针 此时并未指向内容

	InitStack(p);  //创建一个头结点  头指针p指向头结点 头结点指针域数据域都为NULL

	TravelStack(p); //遍历

	Push(p,1);
	Push(p,2);
	Push(p,3);
	Push(p,4);  //压栈

	TravelStack(p);//遍历

	GetTop(p,e); //取栈顶元素
	printf("e = %d\n",e);
	Pop(p,e);//出栈

	TravelStack(p);
	printf("e = %d\n",e);

	DestroyStack(p);//销毁

	printf("%d\n",StackEmpty(p));
}


//初始化 链栈 创建一个头结点 头结点不存储数据 只起到数据操作的作用
//创建的内存类型为 结构体的形式   初始化
void InitStack(LinkStNode *&s)
{     
	s = (LinkStNode *)malloc(sizeof(LinkStNode));//创建一个头结点 头结点内容和指针域都为NULL
    s->next = NULL;
	s->data = NULL;
}

//判断是否为空,也就是
bool StackEmpty(LinkStNode *s)
{
    return(s->next==NULL);
}

void Push(LinkStNode *&s,ElemType e)
{     
	LinkStNode *p;
    p=(LinkStNode *)malloc(sizeof(LinkStNode));
    p->data=e;		//新建元素e对应的结点*p
    p->next=s->next;	//插入*p结点作为开始结点 头插法

    s->next=p;
}

bool Pop(LinkStNode *&s,ElemType &e)
{     
	LinkStNode *p;
    if (s->next==NULL)		//栈空的情况
		return false;
    p=s->next;			//p指向开始结点 此时p和s->next同时指向下一个也就是第一个节点(首节点即为栈顶 在该处进行压栈出栈操作)
    e=p->data;
    s->next=p->next;		//删除*p结点p->next相当于s->next->next  即为首节点的的下一个节点
    free(p);			//释放*p结点  p和 s->next同时指向栈顶元素  free(p)也就是free(首节点) p为指针变量,并不被free free释放的为内存  也就是结构体LiskStNode的地址

    return true;
}

bool GetTop(LinkStNode *s,ElemType &e)
{      
	if (s->next==NULL)	//栈空的情况
	return false;
    e=s->next->data;
    return true;
}

void DestroyStack(LinkStNode *&s)
{
	LinkStNode *p = s,*q=s->next;
	while(q!=NULL)
	{
		free(p);
		p=q;
		q=q->next;
	}
	free(p);
}

void TravelStack(LinkStNode *&s)
{
	LinkStNode *p=s->next;
	while(p!=NULL)
	{
		printf("%d->",p->data);
		p=p->next;
	}
	printf("NULL\n");

}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构之动态栈、静态栈详解 的相关文章

随机推荐

  • RTP时间戳概念

    RTP协议不依赖于底层协议 由于UDP包的快速 时实性高的特点 它通常和UDP结合在一起 作为UDP的上层载体数据的形式传播 typedef struct rtp header t uint32 t v 2 protocol version
  • Python基础(3)——PyCharm介绍

    Python基础 3 PyCharm介绍 文章目录 Python基础 3 PyCharm介绍 课程目标 一 PyCharm的作用 二 下载和安装 2 1 下载 2 2 安装 三 PyCharm基本使用 3 1 新建项目 3 2 新建文件并书
  • Qt::UserRole的作用

    Qt UserRole相当于一个索引的作用 对于一些Qt的组件有一个参数位置就需要Qt UserRole void QComboBox setItemData int index const QVariant value int role
  • 设计模式笔记1--单例模式

    设计模式笔记1 单例模式 1 单例模式介绍 Head First设计模式中解释 用来创建独一无二的 只能有一个实例的对象的入场券 即 该类只能有一个示例 其实现逻辑一般是 构造函数声明为private或protect防止被外部函数实例化 内
  • 数据学习(十四)-方差分析与试验设计

    1 方差分析引论 2 单因素方差分析 3 方差分析中的多重比较 4 双因素方差分析 5 试验设计初步 1 方差分析引论 方差分析是比较多个总体的均值是否相等 但本质上它所研究的是变量之间的关系 1 1 方差分析及其有关术语 检验多个总体均值
  • 离线图书推荐,使用sparkMLlib ALS协同过滤算法

    图书推荐 使用sparkMLlib ALS协同过滤算法 bookcrossing数据集 Scala语言 虚拟机ieda平台 代码参照HADOOP大数据实战权威指南第十一章 只能说算是能运行出结果了吧 package com csu impo
  • openGauss学习笔记-53 openGauss 高级特性-Ustore

    文章目录 openGauss学习笔记 53 openGauss 高级特性 Ustore 53 1 设计原理 53 2 核心优势 53 3 使用指导 openGauss学习笔记 53 openGauss 高级特性 Ustore Ustore
  • 2021-01-26Taming Transformers for High-Resolution Image Synthesis(arXiv2020)(有代码)

    转自 https blog csdn net amusi1994 article details 112301258 代码链接 https github com CompVis taming transformers 项目主页 https
  • C++中一个类成员函数调用另一个类成员的方法

    在继承之外 在C 中一个类成员函数调用另一个类成员的方法主要有 类的组合 友元类 类的前向声明 单例模式等 下面主要讲讲这4种方法的实现 方法1 利用类的组合 组合通俗来讲就是类B有类A的属性 如声明一个Person类 再声明一个Teach
  • 【前端面试题之Vue篇】(1)父子组件通信方式Props/$emit

    文章目录 前言 一 父组件向子组件传值 1 Props定义 2 Props 用法 1 路由里注册父子组件 2 父组件里引入子组件 并且注册子组件 3 子组件利用props接受父组件传过来的值 4 展示 二 子组件向父组件传值 1 emit
  • SegGPT_分割上下文中的所有内容

    文章目录 摘要 2 相关工作 2 1 视觉分割 2 2 视觉通才 2 3 上下文视觉学习 3 方法 3 1 上下文着色 3 2 上下文组合 3 3 在上下文中调优 4 实验 4 1 数据集 4 2 一阶段训练细节 4 3 定性结果 4 4
  • 30--子类对象的实例化过程

    在继承的操作中 对于子类的实例化也是有要求的 即子类对象在实例化之前必须首先调用父类中的构造方法 然后再调用子类自己的构造方法 实例1 定义父类 package com qwy bean public class Person privat
  • Django 快速搭建博客 第十节(修复首页,阅读量的数据)

    写到这里 我们已经使用django博客基础开发框架什么的快开发到底了 接下来的是django进阶阶段 难度会稍微大一些 这里主要是进行一些遗漏掉的地方的 1 我们博客的首页的点击事件未实现 2 文章的阅读量未填满 对于第一点 我们只要把相应
  • 排序算法概述与算法时间复杂度

    时间频度 算法的时间复杂度 常见的时间复杂度 注意 时间复杂度怎么理解呢 如果一段程序不会因为变量的规模而使得执行次数发生变化 那么时间复杂度就是O 1 比如下面这个代码就不会因为i 200000就会使得代码的执行次数变多 平均时间复杂度和
  • 参数错误。 (异常来自 HRESULT:0x80070057 (E_INVALIDARG))

    异常来自 HRESULT 0x80070057 E INVALIDARG 未能加载程序集 几次删除引用然后重新引用程序集还是报错 奔溃中 网上搜索还真有解决办法 解决方法 是 删除 C WINDOWS Microsoft NET Frame
  • vue 动态添加属性

    Vue set 方法用于设置对象的属性 它可以解决 Vue 无法检测添加属性的限制 语法格式如下 Vue set target key value 参数说明 target 可以是对象或数组 key 可以是字符串或数字 value 可以是任何
  • 北斗+低速自动驾驶机器人来袭,在线发布会精彩抢先看

    北斗 低速自动驾驶机器人来袭 在线发布会精彩抢先看 千寻位置北斗智能市场新品情报局在线沙龙第一期 聚焦低速自动驾驶领域新物种 3月16日15点在线发布3款北斗 机器人 1款高精度组合导航 覆盖农业 零售 环卫 港口多个场景 本期在线沙龙邀请
  • 【漫画】分享16张程序员高端漫画~

    1 编译中 真的不是我偷懒 程序编译那么久 我真的什么都做不了啊 2 sudo 三明治 没有什么是一个 sudo 解决不了的问题 3 新货币 对于我这种表情包大户 分分钟超越西虹市首富 4 电脑病毒范恩图 我擦 电脑突然变得好卡 是不是中病
  • 修改maven项目本地的端口

    在maven 项目中 1 找到web 文件下的pom xml 文件 2 修改pox xml中的port 即可
  • 数据结构之动态栈、静态栈详解

    程序都已经成功通过编译 运行无异常 如果你发现bug 请评论区留言 我们相互交流下经验 定义 栈是只能在一端进行数据的插入与删除的线性表 也可以理解为一种 先进后出 的存储结构 按照内存生成的方式不同分为 静态栈 动态栈 栈的几个概念 允许