【链栈的实现】--------本质为不带头结点的 头插法建立起来的单链表

news2024/9/20 9:09:50

1.链栈的基本属性与特征:

链栈是运算受限的单链表,只能在链表头部进行操作
在这里插入图片描述

2.链栈的相关基础操作汇总

  • 初始化操作:操作结果:构造一个空栈 S。
InitStack(LinkStack *s)
  • 判定S是否为空栈:
    初始条件:栈S 已存在
    操作结果:若栈S为空栈,则返回TRUE,否则 FALSE.
StackEmpty(LinkStack s)
  • 求栈的长度
    初始条件:栈S 已存在。
    操作结果:返回S的元素个数,即栈的长度
StackLength(LinkStack s)
  • 栈销毁操作:
    初始条件:栈S 已存在。
    操作结果:将S清为空栈,且不使用该栈顶指针。
DestroyStack(LinkStack* s) 
  • 入栈操作(重点)
    初始条件:栈S已存在 (链栈一般不存在栈满的情况)
    操作结果:插入元素e为新的栈顶元素。
Push(LinkStack *s,SElemType e)
  • 出栈操作(重点)
    初始条件:栈S 已存在且非空:
    操作结果:删除S的栈顶元素a.,并用e返回其值。
Pop(LinkStack* s, SElemType* e)
  • 取栈顶元素
    初始条件:栈S 已存在且非空。
    操作结果:用e返回S的栈顶元素,并销毁该结点空间。
GetTop(LinkStack s, SElemType* e)
  • 从栈顶开始输出整栈
    初始条件:栈S 已存在且非空。
    操作结果:从栈顶输出栈中所有元素。
PrintStack(LinkStack s)

3.链栈的整栈创建(不带头结点,含头指针 即栈顶指针)

3.1链栈的结构定义:

//1.链栈的结构定义:
//实际上就是运算受限的单链表,只能在链表头部进行操作
typedef struct StackNode
{
	SElemType data;
	struct StackNode* next;
}StackNode,*LinkStack;
//StackNode:用来定义单个链栈的结点
//*LinkStack:用来定义整个链栈(即链栈的头指针或者栈顶指针)
  • 在结构定义中,一个链栈结点里包含了,指针域,和存放栈元素数据域这两个部分
  • 而别名StackNode用来定义单个链栈结点
    LinkStack 用来定义链栈的头指针(栈顶指针)

3.2链栈的初始化

注意:传入参数时,需传入二级指针,这样才能修改栈中的数据

bool InitStack(LinkStack *s)
3.2.1算法步骤:

(1)将传入的头指针(栈顶指针)初始化为空,表示空栈

//2.链栈的初始化
bool InitStack(LinkStack *s)
{
	//[1]将传入的头指针初始化为空,表示空栈
	*s = NULL;
	return true;
}

3.3判断链栈是否为空

注意:传入参数时,不需要传入二级指针,因为判空操作只需要访问栈顶指针,并不做修改

bool StackEmpty(LinkStack s)
3.3.1算法步骤:

(1)若传入的头指针(栈顶指针)为空,则返回true,表示空栈;否则栈非空

//3.判断链栈是否为空
bool StackEmpty(LinkStack s)
{
	//[1]若传入的头指针为空,则返回true,表示空栈
	if (s == NULL)
	{
		return true;
	}
	else
	{
		return false;
	}
}

3.4求链栈的长度

注意:传入参数时,不需要传入二级指针,因为求长度只需要返回计数器的结果,并不修改栈中数据

  • 该操作与求单链表长度的操作完全一致
int StackLength(LinkStack s)
3.4.1算法步骤:

(1)定义一个临时指针p,并让其指向首元结点
(2)初始化计数器为0,因为没有排除空链栈的可能
(3)循环遍历,统计结点数

//4.求链栈的长度
int StackLength(LinkStack s)
{
	//[1]定义一个临时指针p,并让其指向首元结点
	StackNode * p = s;
	//[2]初始化计数器为0,因为没有排除空链栈的可能
	int count = 0;

	//[3]循环遍历,统计结点数
	while (p != NULL)//若为空,直接返回i=0,不会进入循环
	{
		count++;//进入循环,证明存在首元结点,故先叠加
		p = p->next;//当p指向空,计数器已经将表长记录成功
	}

	return count;
}

3.5销毁链栈

注意:传入参数时,需要传入二级指针,因为销毁栈需要将栈顶指针置空

bool DestroyStack(LinkStack* s) 
3.5.算法步骤

(1)定义临时指针p,用来链栈中遍历所有结点
(2)定义临时指针temp,temp用来销毁空间
(3)循环销毁每个结点
(4)循环结束后,将头指针置为空,表示空栈(同时避免了悬挂指针问题)

//5.销毁链栈
//注意:这里传入二级指针,因为最终需要将头指针置为空
bool DestroyStack(LinkStack* s) 
{
	//[1]定义临时指针p,用来链栈中遍历所有结点
	StackNode* p = *s;

	//[2]定义临时指针temp,temp用来销毁空间
	StackNode* temp;

	//[3]循环销毁每个结点
	while (p != NULL) 
	{
		temp = p;
		p = p->next;
		free(temp);
	}

	//[4]循环结束后,将头指针置为空,表示空栈(同时避免了悬挂指针问题)
	*s = NULL;
	return true;
}

3.6链栈的入栈(核心1)

注意:传入参数时,需要传入二级指针,因为入栈操作需要 更改栈顶指针以及 添加栈顶元素

  • 本质为单链表的头部插入操作
  • 由于链栈一般不存在栈满的情况,故不需要特殊判断
bool Push(LinkStack *s,SElemType e)
3.6.1算法步骤:

(1)建立新结点,并初始化数据域
(2)入栈操作:
<1>修改新结点的指针域,指向当前栈顶元素
<2>修改栈顶指针,栈顶指针应指向新插入的结点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//6.链栈的入栈操作
//本质为单链表的头部插入操作
bool Push(LinkStack *s,SElemType e)
{
	//[1]建立新结点,并初始化数据域
	StackNode* p = (StackNode *)malloc(sizeof(StackNode));

	if (p == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	p->data = e;


	//[2]将新结点插入到链栈的头部
	p->next = (*s);//仅需要修改新结点的指针域即可
	*s = p;//注意修改栈顶指针,栈顶指针应指向新插入的结点
	
	return true;
}

3.7链栈的出栈(核心2)

注意:传入参数时,需要传入二级指针,因为出栈操作需要 更改栈顶指针以及 带出栈顶元素

bool Pop(SqStack* s, SElemType* e)
3.7.1算法步骤:

(1)判断是否栈空(即栈顶指针 是否指向NULL)
(2)出栈操作:
<1>先将当前栈顶指针指向的栈顶元素 赋值给e
<2>再定义并初始化临时指针p指向当前栈顶结点
<3>修改栈顶指针指向出栈后的栈顶结点
<4>销毁临时指针p指向的结点空间

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//7.链栈的出栈操作
//本质为单链表的头部删除操作
bool Pop(LinkStack* s, SElemType* e)
{
	//[1]判断链栈是否为空,为空则返回false
	if ((*s) == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]保存栈顶结点的数据
	*e = (*s)->data;

	//[3]定义并初始化临时指针p指向当前栈顶结点
	StackNode* p=(*s);

	//[4]修改栈顶指针指向出栈后的栈顶结点
	(*s) = (*s)->next;

	//[5]销毁临时指针p指向的结点空间
	free(p);

	return true;
}

3.8取栈顶元素

注意:传入参数时,不需要传入二级指针,因为只取元素,不修改栈的数据

bool GetTop(LinkStack s, SElemType* e)
3.8.1算法步骤:

(1)判断链栈是否为空,为空则返回false
(2)若链栈不为空,则直接返回栈顶元素
(栈顶元素为链表的首元结点,即链表的头指针指向的结点)

在这里插入图片描述

//8.获取栈顶元素
bool GetTop(LinkStack s, SElemType* e)
{
	//[1]判断链栈是否为空,为空则返回false
	if (s == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]若链栈不为空,则直接返回栈顶元素
	//注意:栈顶元素为链表的首元结点,即链表的头指针指向的结点
	*e = s->data;

	return true;
}

3.9输出链栈中的所有元素(从顺序栈顶开始输出)

bool PrintStack(LinkStack s)
3.9.1算法步骤:

(1)定义临时指针p,用来链栈中遍历所有结点
(2)判断链栈是否为空,为空则返回false
(3)p从栈顶开始,向栈底移动并依次输出栈中的元素

//9.输出链栈中的所有元素(从栈顶开始输出)
bool PrintStack(LinkStack s)
{
	//[1]定义临时指针p,用来链栈中遍历所有结点
	StackNode* p = s;

	//[2]判断链栈是否为空,为空则返回false
	if (p == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]循环输出每个结点
	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}

	printf("\n");
	return true;
}

4.所有操作如下

//创建一个不带头结点的链栈(头指针就是栈顶指针)
//本质为限制了插入与删除操作的(头插法建立起来的 单链表)
#include<stdio.h>
#include<stdlib.h>


#define bool char
#define true 1
#define false 0

typedef int SElemType;//栈中数据元素的类型定义     

//1.链栈的结构定义:
//实际上就是运算受限的单链表,只能在链表头部进行操作
typedef struct StackNode
{
	SElemType data;
	struct StackNode* next;
}StackNode,*LinkStack;
//StackNode:用来定义单个链栈的结点
//*LinkStack:用来定义整个链栈(即链栈的头指针或者栈顶指针)



//2.链栈的初始化
bool InitStack(LinkStack *s)
{
	//[1]将传入的头指针初始化为空,表示空栈
	*s = NULL;
	return true;
}


//3.判断链栈是否为空
bool StackEmpty(LinkStack s)
{
	//[1]若传入的头指针为空,则返回true,表示空栈
	if (s == NULL)
	{
		return true;
	}
	else
	{
		return false;
	}
}



//4.求链栈的长度
int StackLength(LinkStack s)
{
	//[1]定义一个临时指针p,并让其指向首元结点
	StackNode * p = s;
	//[2]初始化计数器为0,因为没有排除空链栈的可能
	int count = 0;

	//[3]循环遍历,统计结点数
	while (p != NULL)//若为空,直接返回i=0,不会进入循环
	{
		count++;//进入循环,证明存在首元结点,故先叠加
		p = p->next;//当p指向空,计数器已经将表长记录成功
	}

	return count;
}

//5.销毁链栈
//注意:这里传入二级指针,因为最终需要将头指针置为空
bool DestroyStack(LinkStack* s) 
{
	//[1]定义临时指针p,用来链栈中遍历所有结点
	StackNode* p = *s;

	//[2]定义临时指针temp,temp用来销毁空间
	StackNode* temp;

	//[3]循环销毁每个结点
	while (p != NULL) 
	{
		temp = p;
		p = p->next;
		free(temp);
	}

	//[4]循环结束后,将头指针置为空,表示空栈(同时避免了悬挂指针问题)
	*s = NULL;
	return true;
}


//6.链栈的入栈操作
//本质为单链表的头部插入操作
bool Push(LinkStack *s,SElemType e)
{
	//[1]建立新结点,并初始化数据域
	StackNode* p = (StackNode *)malloc(sizeof(StackNode));

	if (p == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	p->data = e;


	//[2]将新结点插入到链栈的头部
	p->next = (*s);//仅需要修改新结点的指针域即可
	*s = p;//注意修改栈顶指针,栈顶指针应指向新插入的结点
	
	return true;
}

//7.链栈的出栈操作
//本质为单链表的头部删除操作
bool Pop(LinkStack* s, SElemType* e)
{
	//[1]判断链栈是否为空,为空则返回false
	if ((*s) == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]保存栈顶结点的数据
	*e = (*s)->data;

	//[3]定义并初始化临时指针p指向当前栈顶结点
	StackNode* p=(*s);

	//[4]修改栈顶指针指向出栈后的栈顶结点
	(*s) = (*s)->next;

	//[5]销毁临时指针p指向的结点空间
	free(p);

	return true;
}


//8.获取栈顶元素
bool GetTop(LinkStack s, SElemType* e)
{
	//[1]判断链栈是否为空,为空则返回false
	if (s == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]若链栈不为空,则直接返回栈顶元素
	//注意:栈顶元素为链表的首元结点,即链表的头指针指向的结点
	*e = s->data;

	return true;
}

//9.输出链栈中的所有元素(从栈顶开始输出)
bool PrintStack(LinkStack s)
{
	//[1]定义临时指针p,用来链栈中遍历所有结点
	StackNode* p = s;

	//[2]判断链栈是否为空,为空则返回false
	if (p == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]循环输出每个结点
	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}

	printf("\n");
	return true;
}

int main()
{
	LinkStack s1;
	InitStack(&s1);  // 初始化栈

	// 入栈
	Push(&s1, 10);
	Push(&s1, 20);
	Push(&s1, 30);
	Push(&s1, 31);
	Push(&s1, 32);

	printf("当前栈的长度: %d\n", StackLength(s1));
	printf("\n");

	// 获取栈顶元素
	SElemType ee;
	GetTop(s1, &ee);
	printf("当前栈顶元素为:%d\n", ee);

	printf("\n");
	printf("从当前栈顶元素向下输出:\n");
	
	PrintStack(s1);

	// 出栈
	printf("\n");
	SElemType e[10];
	for (int i = 0; i < 3; i++)
	{
		Pop(&s1, &e[i]);
		printf("出栈元素: %d\n", e[i]);
	}

	printf("\n");
	printf("从当前栈顶元素向下输出:\n");
	PrintStack(s1);

	// 销毁栈
	DestroyStack(&s1);

	printf("栈是否为空: \n");
	if (StackEmpty(s1))
	{
		printf("栈为空!\n");
	}
	return 0;
}

在这里插入图片描述

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

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

相关文章

【Linux】全面解析进程:优先级、环境变量与地址空间

文章目录 1. 进程概念1.1 什么叫做进程&#xff1f;1.2 进程和程序的区别 2. linux下的进程2.1 task_struct 包含哪些内容2.2 访问&#xff08;查看&#xff09;进程信息2.3 通过系统调用获取进程标示符2.4 通过系统调用创建进程2.5 进程状态2.6 如何查看进程状态&#xff08;指…

鸿蒙开发5.0【基于CameraKit,通过avrecorder进行录像】

1 场景描述 录像是相机应用的最重要功能之一&#xff0c;录像是循环帧的捕获。本文通过CameraKit自定义相机并通过avrecorder进行录像。 2 效果图 3 自定义相机录像流程图 4 方案描述 4.1 整体描述&#xff1a; 总体可分为:1、相机输入&#xff0c;2、同时输出预览流录像流…

【Python机器学习】NLP分词——利用分词器构建词汇表(三)——度量词袋之间的重合度

如果能够度量两个向量词袋之间的重合度&#xff0c;就可以很好地估计他们所用词的相似程度&#xff0c;而这也是它们语义上重合度的一个很好的估计。因此&#xff0c;下面用点积来估计一些新句子和原始的Jefferson句子之间的词袋向量重合度&#xff1a; import pandas as pdse…

win10配置安装apache服务

Welcome! - The Apache HTTP Server Project

redisson watchdog 原理

目录 1、使用2、加锁解析1、getLock2、tryLock2.1、当ttl为null时为加锁成功&#xff0c;返回true,否则继续往下执行&#xff0c;判断是否超过等待时间&#xff0c;当前时间减去获取锁前时间就是获取锁花费时间。2.2、tryAcquire(leaseTime, unit, threadId)2.3 、renewExpirat…

黑悟空!一区预定!原创首发!SLWCHOA-Transformer-LSTM混合改进策略的黑猩猩优化算法多变量时间序列预测

黑悟空&#xff01;一区预定&#xff01;原创首发&#xff01;SLWCHOA-Transformer-LSTM混合改进策略的黑猩猩优化算法多变量时间序列预测 目录 黑悟空&#xff01;一区预定&#xff01;原创首发&#xff01;SLWCHOA-Transformer-LSTM混合改进策略的黑猩猩优化算法多变量时间序…

帮助检测SQL注入漏洞的工具

目录 SQLMap与Burp Suite相比&#xff0c;哪个更适合进行大规模的SQL注入检测&#xff1f; OWASP ZAP在检测SQL注入时的优势体现在哪些方面&#xff1f; 对于SQL注入漏洞检测&#xff0c;Havij和acunetix有什么区别&#xff1f; 在检测SQL注入漏洞方面&#xff0c;有几款工具…

shell脚本-采集容器内自定义端口tcp连接数并通过http接口推送到Prometheus

目录 1、脚本编写 2、脚本说明 3、运行脚本 1、脚本编写 脚本监控服务器 5000 端口的 TCP 连接数。使用 netstat 工具获取连接数&#xff0c;并通过一个简单的 shell 服务器提供 /connect 接口。具体功能如下&#xff1a; vim prometheus_tcp_monitor.sh 编写脚本&#…

Docker 安装消息队列RabbitMQ

拉取镜像 docker pull rabbitmq拉取最新镜像 创建并运行 docker run -d --hostname my-rabbit --name rabbit-p 15672:15672 -p 5673:5672rabbitmq开启Web管理 进入容器 docker exec -it rabbitmq /bin/bash开启web管理 rabbitmq-plugins enable rabbitmq_managementhttp:…

一文通透DeepSeek-V2(改造Transformer的中文模型):从DeepSeek LLM到DeepSeek-V2的MLA与MoE

前言 成就本文有以下三个因素 24年5.17日&#xff0c;我在我司一课程「大模型与多模态论文100篇」里问道&#xff1a;大家希望我们还讲哪些论文 一学员朋友小栗说&#xff1a;幻方发布的deepseek-v224年5.24日&#xff0c;我司一课程「大模型项目开发线上营1」里的一学员朋友…

Typora + PicGo + SMMS 实现markdown格式文档图片上传

Typora PicGo SMMS 实现图片自动上传 1. Typora 软件安装2. PicGo 的安装2.1 下载 PicGo 3. 配置 SMMS 图床服务3.1 注册并登录 SMMS3.2 获取 API Token 4. 软件配置4.1 Typora 图床设置4.2 PicGo 配置 5. 使用 Typora 实现图片自动上传 1. Typora 软件安装 Typora中文版是一…

排序1

一、概述 直接插入排序 是稳定排序 二、插入排序 1&#xff09;直接插入排序 2&#xff09;折半插入排序 3)希尔排序 、 三、交换排序 1&#xff09;冒泡排序 2&#xff09;快速排序

three.js 着色器学习 聚集地

预览地址&#xff1a;https://z2586300277.github.io/three-cesium-examples 国内站点预览&#xff1a;http://threehub.cn github: https://github.com/z2586300277/three-cesium-examples

swift微调Qwen-7B大模型

环境说明&#xff1a; CUDA相关环境已搭建完成&#xff0c;不会装CUDA环境可参照我的其它文章&#xff1b; 显卡&#xff1a;4张3090 1、安装swift环境 #从源码安装 git clone https://github.com/modelscope/swift.git cd swift pip install -e .[llm] pip install -e .[e…

文件读写与缓存机制

文件读写与缓存机制 写文件&#xff1a; 1&#xff1a;stdio函数库 fopen/fwrite/fflush/fclose File* fp2: POSIX系统级别函数 open/write/close fd3&#xff1a;Windows系统级别函数 CreateFile/WriteFile/CloseHandle**同步数据到磁盘&#xff1a;**FlushFileBuffers、f…

永磁同步电机高性能控制算法(13)后续篇—— 基于高阶扩张状态观测器(ESO)的无模型预测控制(MFPC)

1.前言 前文已经介绍过了高阶ESO相对于传统ESO的优势。 https://zhuanlan.zhihu.com/p/703039702https://zhuanlan.zhihu.com/p/703039702 但是当时搭的ESO有点问题。把公式修正之后&#xff0c;发现前文用的改进四阶ESO无法使用。 今天来解释一下为什么改进4阶ESO无法使用…

SystemTap(stap)架构和原理介绍,以及脚本编写举例

1 SystemTap简介 SystemTap是一个诊断Linux系统性能或功能问题的开源工具。它允许开发人员和系统管理员深入研究内核甚至用户空间应用程序的行为&#xff0c;以便发现错误状态、性能问题&#xff0c;或者仅仅为了解系统是如何工作的。它使得对运行时的Linux系统进行诊断调式变…

递归算法及应用

一.简介 1.介绍 递归&#xff08;Recursion&#xff09;在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法&#xff0c;其核心思想是分治策略。 在日常开发中&#xff0c;我们使用循环语句远远大于递归&#xff0c;但这不能说明递归就没有用武之地&am…

服务器(百度云)部署项目(jar包)

java项目打包成jar包&#xff1a;clean------compile------install jar包上传到服务器上 和jar包相同的文件里&#xff0c;创建Dockerfile文件。 Dockerfile文件的内容informationerasure是jar包名&#xff0c;这里可根据自己定义的名字进行更换。 Dockerfile文件内容&#x…

chrome打印dom节点不显示节点信息

正常直接console dom节点 代码改成 var parser new DOMParser(); var docDom parser.parseFromString(testHtml, text/html); console.log(docDom) let htmlHeader ref< HTMLElement | null>(null) let htmlBoby ref< HTMLElement | null>(null) htmlHeader.v…