2.数据结构-栈和队列

数据结构-栈和队列

  • 2.1栈
    • 2.1.1栈的表示和实现
    • 2.1.2栈的应用举例
      • 数制转换
      • 括号匹配检验
      • 迷宫给求解
      • 表达式求值
    • 2.1.3链栈的表示和实现
    • 2.1.4栈与递归的实现
      • 遍历输出链表中各个结点的递归算法
      • *Hanoi塔问题的递归算法
  • 2.2队列
      • 2.2.1循环队列——队列的顺序表示和实现
      • 2.2.2链队——队列的链式表示和实现

2.1栈

栈是限定仅在表尾进行插入或删除操作的线性表,因此,对栈来说,表尾端有其特殊含义,称为栈顶(top),相应地,表头称为栈底

在这里插入图片描述
E为栈底元素,A为栈顶元素。也就意味着,栈的修改是按后进先出的原则进行的。因此栈又称为后进先出线性表(简称LIFO结构)。

2.1.1栈的表示和实现

和线性表类似,栈也有两种存储表示方式。
顺序栈,即栈的顺序存储结构是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置。通常的习惯做法是top=0表示空栈,鉴于C语言中数组的下标约定从0开始,则当以C作描述语言时,如设定会带来很大不便;另一方面,由于栈在使用过程中所需最大空间的大小很难估计,一般来说,在初始化设空栈时不应限定栈的最大容量。一个较合理的做法是;先分配一个基本容量,然后在应用过程中,当栈的空间不够使用时再逐段扩大。为此可设定两个常量:STACK_INIT_SIZE(存储空间初始分配量)和STACKINCREMENT(存储空间分配增量)。

top 并不是栈顶元素,而是指向栈顶元素的下一个位置。
top - 1 才是真正的栈顶元素位置。

typedef struct{
	SElemType *base; //栈底指针
	SElemType *top;  //栈顶指针,其初值指向栈底
	int stacksize;   //栈当前可使用的最大容量
	//每当插入心得栈顶元素时,指针top+1
};

顺序栈所有操作汇总:

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

#define SElemType  int
#define STACK_INIT_SIZE 100 //存储空间初始分配量 
#define STACKINCREMENT 10   //存储空间分配增量
#define Status int

typedef struct{
	SElemType *base;
	SElemType *top;
	int stacksize;
} SqStack; 

Status InitStack (SqStack &S){
	//构造一个空栈
	S.base = (SElemType *) malloc(STACK_INIT_SIZE * sizeof(SElemType));
	
	if(!S.base) exit(0); //存储分配失败
	S.top = S.base;
	S.stacksize =  STACK_INIT_SIZE;
	return 1;
}

Status Push(SqStack &S, SElemType e){
	//插入元素e为新的栈顶元素
	if(S.top - S.base >= S.stacksize){ //栈满.追加存储空间 
		S.base = (SElemType *) realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));
		if(!S.base) exit(0); //存储分配失败
		S.top = S.base + S.stacksize;
		S.stacksize += STACKINCREMENT; 
	} 
	*S.top++ = e;
	return 1;
}

Status Pop(SqStack &S, SElemType &e){
	//若栈不为空,则删除S的栈顶元素,用e返回其值
	if(S.top == S.base) return 0; //空栈
	e = * --S.top; //先将S.top赋值给e,再自减
	
	return 1; 
}

Status GetTop(SqStack &S, SElemType &e){
	//若栈不为空,则用e返回S的栈顶元素
	if(S.top == S.base) return 0;
	e = *(S.top - 1);
	return 1; 
}

int StackLength(SqStack &S){
	//返回栈的元素个数,即栈的长度 
	return S.top - S.base;
}

int StackEmpty(SqStack &S){
	//返回栈是否为空,为空返回0,不为空返回1
	if(!StackLength(S)) return 0;
	return 1; 
}

void ClearStack(SqStack &S){
	//把S置为空栈
	S.top = S.base; 
}

void DestroyStack(SqStack &S){
	//销毁栈 
	if(S.base){
		free(S.base);
		S.base = S.top = NULL;
		S.stacksize = 0;
	}
}

int main()
{
	SqStack S;
	InitStack(S);
	Push(S, 1);
	Push(S, 2);
	int e; 
	Pop(S, e);
	printf("%d\n", e);
	GetTop(S, e);
	printf("%d\n", e);
	int l = StackLength(S);
	printf("length: %d\n", l);
	
	return 0; 
}

2.1.2栈的应用举例

数制转换

将十进制数N和其他d进制数的转换。算法原理如下:
N = ( N / d ) ∗ d + N    m o d    d N = (N/d)*d+N \ \ mod \ \ d N=(N/d)d+N  mod  d
要求: 对于输入的任意一个非负十进制整数,打印与其等值的八进制数。

//栈的操作与2.1.1中代码相同
int main()
{
	SqStack S;
	InitStack(S);
	printf("请输入一个非负的十进制数: ");
	int numT;
	scanf("%d", &numT);
	while(numT){
		Push(S, numT % 8);
		numT /= 8;
	} 
	int e;
	while(StackEmpty(S)){
		Pop(S, e);
		printf("%d", e);
	}
	
	return 0; 
}

括号匹配检验

假设表达式中允许包含两种括号:圆括号和方括号,其嵌套顺序随意,即()为正确格式,[{]为不正确格式。
要求: 输入只含圆括号和方括号的字符串,判断其是否是正确的格式。

//要加上#include<string.h> 
int main()
{
	SqStack S;
	InitStack(S);
	char str[100];
	printf("输入只含圆括号和方括号的字符串: ");
	scanf("%s", str);
	int length = strlen(str), i, flag = 1, e;
	//(为1, )为2, [为3, ]为4 
	for(i = 0; i < length; i ++){
		if(str[i] == '[') Push(S, 3);
		if(str[i] == '(') Push(S, 1);
		
		if(str[i] == ')') {
			GetTop(S, e);
			if(e == 1) Pop(S, e);
		} 
		if(str[i] == ']') {
			GetTop(S, e);
			if(e == 3) Pop(S, e);
		}
	}
	
	if(!StackEmpty(S)) printf("%s是合法字符串\n", str);
	else printf("%s不是合法字符串\n", str);
	
	return 0; 
}

迷宫给求解

链接: C语言 严蔚敏 数据结构 迷宫求解_顺序栈应用

类似于深度优先搜索,用栈去模拟这个过程。

表达式求值

链接: C语言 严蔚敏 数据结构 表达式求值_顺序栈应用
类似于括号匹配检验。

2.1.3链栈的表示和实现

链栈是指采用链式存储结构实现的栈。通常链栈用单链表来实现。
在这里插入图片描述
由于栈的主要操作是在栈顶插入和删除,显然以链表的头部作为栈顶是最方便的。

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

#define List_Init_Size 100 //线性表存储空间的初始分配量
#define ListIncreMent 10   //线性表存储空间的分配增量
#define ElemType int
#define Status int

typedef struct StackNode{
    ElemType data;
    struct StackNode *next;
}StackNode, *LinkStack;

Status InitStack(LinkStack &S) {
    // 构造一个空栈,栈顶指针为空
    S = NULL;
    return 1;
}

Status Push(LinkStack &S, ElemType e) {
    //和顺序栈的入栈不同,链栈的入栈前不需要判断栈是否满,只需要为入栈动态分配一个节点空间
    StackNode *p = (StackNode *)malloc(sizeof(StackNode)); // 使用 malloc 分配内存
    if (p == NULL) {
        // 内存分配失败的处理
        return 0;
    }
    p->data = e;
    p->next = S;
    S = p;  // 栈顶指针指向新节点
    return 1;
}

Status Pop(LinkStack &S, ElemType &e) {
    if (S == NULL) {
        return 0;  // 空栈,返回失败
    }
    e = S->data;  // 获取栈顶元素
    StackNode *p = S;  // 声明指针 p,指向栈顶元素
    S = S->next;  // 将栈顶指针指向下一个元素
    free(p);  // 释放栈顶元素的内存
    return 1;  // 返回成功
}

ElemType GetTop(LinkStack S){
	//返回S的栈顶元素 
	if(S != NULL) return S->data;
}

int main()
{
	LinkStack S;
	InitStack(S);
	int i, e;
	for(i = 1; i <= 10; i ++){
		Push(S, i);
	}
	Pop(S, e);
	printf("%d\n", e);
} 

2.1.4栈与递归的实现

对于链表,其结点LNode的定义由数据域data和指针域next组成,而指针域next是一种指向LNode类型的指针,即LNode的定义中又用到了其自身所以链表是一种递归的数据结构。

遍历输出链表中各个结点的递归算法

算法从前向后遍历输出链表结点的递归算法,直到p为NULL时递归结束,在递归过程中,p不断指向后续节点。

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

#define List_Init_Size 100 //线性表存储空间的初始分配量
#define ListIncreMent 10   //线性表存储空间的分配增量
#define ElemType int
#define Status int

typedef struct LNode{
    ElemType      data;
    struct LNode  *next;
}LNode, *LinkList;

void CreateList_L(LinkList &L, int n)
{
    // 尾插法
    L = (LinkList) malloc(sizeof (LNode));
    L->next = NULL;    
    LinkList tail = L;  // tail指向链表的尾部
	printf("请输入\n");                                 //先建立一个带头结点的单链表
    for (int i = n; i > 0; --i)
    {
        LinkList p = (LinkList) malloc(sizeof (LNode)); //生成新结点
        scanf("%d", &p->data);
		p->next = NULL;
		tail->next = p;
		tail = p;
    }
}
void TraverseList(LinkList p){
	if(p == NULL) return;
	else
	{
		printf("%d\n", p->data);
		TraverseList(p->next);
	}
}
int main()
{
	LinkList L;
	int i, e;
	CreateList_L(L, 10);
	
	TraverseList(L->next);
	
	return 0;
} 

*Hanoi塔问题的递归算法

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

int m = 0;

void move(char A, int n, char C){ //将编号为n的圆盘从A移到C 
	printf("%d, %d, %c, %c\n", ++m, n, A, C);
}

void Hanoi(int n, char A, char B, char C){
	if(n == 1) move(A, 1, C); //将编号为1的圆盘从A移到C
	else
	{
		Hanoi(n-1, A, C, B); //将A上编号1~n-1的圆盘移到B, C做辅助塔
		move(A, n, C);       //将编号为n的圆盘从A到C
		Hanoi(n-1, B, A, C); //将B上编号为1~n-1的圆盘移到C, A做辅助塔	
	} 
}

int main()
{
	int n = 3;
	Hanoi(n, 'A', 'B', 'C');
	
	return 0;
} 

递归算法的时间复杂度为 O ( 2 n ) O(2^n) O(2n),空间复杂度则为 O ( n ) O(n) O(n)

2.2队列

队列的操作与栈的操作类似,不同的是,删除实在表的头部(即队头)进行。

队列(Queue)是一种 先进先出(FIFO,First-In-First-Out)的线性数据结构,它的基本特点是元素的插入发生在队尾,而元素的删除发生在队头。队列通常用来处理需要按照顺序处理的数据,类似于排队的过程。

2.2.1循环队列——队列的顺序表示和实现

队列也有两种表示,顺序表示和链式表示。

在队列的顺序存储结构中,除了用一组连续的存储单元依次存放从队列头到队列尾的元素外,尚需要附设两个整型变量front和rear分别指示队列头元素和队列尾元素的位置。在初始化创建空队列时,令front = rear = 0,每当插入新的队列尾元素,尾指针+1,每当删除队列头元素
时,头指针front+1。在非空队列中头指针始终指向队列头元素,而尾指针始终指向队列尾元素的下一个位置

为了避免队列实际空间并未占满而出现“假溢出现象”,我们将顺序队列变为一个环状的空间,称为循环队列。头尾指针以及队列元素之间关系不变,只是在循环队列中,头尾指针“依环状态增1”的操作可用“模”运算来实现。通过取模,头指针和尾指针就可以在顺序表空间内以头尾衔接的方式“循环移到”。

假溢出现象解释:就是因为删除操作会让头指针+1,导致头指针之前的空间就不会被利用起来,而当尾指针的值到达队列的空间最大值时,就会产生假溢出了。

如何判断队列是否为空或者队列是否已满
少用一个元素空间,即队列空间大小为m时,有m-1个元素就认为是队满。即当头尾指针的值相同时,则认为队空;而当尾指针在循环意义上+1后是等于头指针则认为队满。
队空的条件: Q . f r o n t = Q . r e a r Q.front = Q.rear Q.front=Q.rear
队满的条件:$ Q.front = (Q.rear+1)%MAXQSIZE $

#include<iostream>

#define MAXQSIZE 100
#define QElemType int
#define Status int

using namespace std;

typedef struct{
    QElemType *base;  // 存储空间的基地址
    int front;         // 队头指针
    int rear;          // 队尾指针
} SqQueue;

Status InitQueue(SqQueue &Q) {
    // 构造一个空队列Q
    Q.base = new QElemType[MAXQSIZE];
    if(!Q.base) exit(0);
    Q.front = Q.rear = 0;
    return 1;  // 返回成功
}

int QueueLength(SqQueue Q){
    //返回Q的元素个数
    return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;
}

Status EnQueue(SqQueue &Q, QElemType e){
    //插入元素e为Q的新的队尾元素
    if((Q.rear + 1) % MAXQSIZE == Q.front){ //队满
        return 0;
    }
    Q.base[Q.rear] = e;
    Q.rear = (Q.rear + 1) % MAXQSIZE;
    return 1;
}

Status DeQueue(SqQueue &Q, QElemType &e){
    //删除Q的队头元素,用e返回其值
    if(Q.front == Q.rear) return 0; //队空
    e = Q.base[Q.front];
    Q.front = (Q.front + 1) %MAXQSIZE;
    return 1;
}

QElemType GetHead(SqQueue Q){
    //返回Q的队头元素,不修改队头指针
    if(Q.front != Q.rear) return Q.base[Q.front];
}


int main()
{
	SqQueue Q;
	InitQueue(Q);
	for(int i = 1; i <= 10; i ++){
	    EnQueue(Q, i);
	}
	int e = GetHead(Q);
	cout << e << endl;
	
	return 0;
	
} 

2.2.2链队——队列的链式表示和实现

在这里插入图片描述
一个链队需要两个分别指示队头和队尾的指针才能唯一确定,给链队添加一个头结点,并令指针始终指向头结点。
链队的操作即为单链表插入和删除操作的特殊情况,只是需进一步修改尾指针或头指针。
在这里插入图片描述
这个图对于理解链队很重要

#include<iostream>

#define MAXQSIZE 100
#define QElemType int
#define Status int

using namespace std;

typedef struct QNode{
    QElemType data;
    struct QNode *next;
} QNode, *QueuePtr; //链队的每个结点

typedef struct{
    QueuePtr front;
    QueuePtr rear;
}LinkQueue; //这就是链队的头指针

Status InitQueue(LinkQueue &Q){
    //构造一个空队列Q
    Q.front = Q.rear = new QNode;
    Q.front->next = NULL;
    return 1;
}

Status EnQueue(LinkQueue &Q, QElemType e){
    //插入元素e为Q的队尾元素
    QueuePtr p = new QNode; //新节点分配内存 **
    p->data = e;
    p->next = NULL;
    Q.rear->next = p;
    Q.rear = p;  //类似尾插法
    return 1;
}

Status DeQUeue(LinkQueue &Q, QElemType &e){
    //删除Q的队头元素
    if(Q.front == Q.rear) return 0; //空队
    QueuePtr p = Q.front;
    e = p->data;
    Q.front->next = p->next;  //修改头指针
    if(Q.rear == p) Q.rear = Q.front; //删除的最后一个元素
    delete p;
    return 1;
}

QElemType GetHead(LinkQueue Q){
    //返回Q的队头元素,不修改队头指针
    if(Q.front != Q.rear)
        return Q.front->next->data;
}

int main()
{
	LinkQueue Q;
	InitQueue(Q);
	for(int i = 1; i <= 10; i ++){
	    EnQueue(Q, i);
	}
	cout << GetHead(Q) << endl;
	return 0;
	
} 

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(十七) Nginx解析:架构设计、负载均衡实战与常见面试问题

什么是Nginx? Nginx 是一款高性能的 HTTP 服务器和反向代理服务器&#xff0c;同时支持 IMAP/POP3/SMTP 协议。其设计以高并发、低资源消耗为核心优势&#xff0c;广泛应用于负载均衡、静态资源服务和反向代理等场景。 一、Nginx 的核心优势 高并发处理能力采用异步非阻塞的…

Cpu100%问题(包括-线上docker服务以及Arthas方式进行处理)

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

【大模型】WPS 接入 DeepSeek-R1详解,打造全能AI办公助手

目录 一、前言 二、WPS接入AI工具优势​​​​​​​ 三、WPS接入AI工具两种方式 3.1 手动配置的方式 3.2 Office AI助手 四、WPS手动配置方式接入AI大模型 4.1 安装VBA插件 4.1.1 下载VBA插件并安装 4.2 配置WPS 4.3 WPS集成VB 4.4 AI助手效果测试 4.5 配置模板文…

架构思维:高性能架构_01基础概念

文章目录 概述基础概念性能指标利特尔法则&#xff08;O T L&#xff09;系统优化策略1. 降低耗时&#xff08;L↓&#xff09;2. 增加容量&#xff08;O↑&#xff09;3. 增加时延&#xff08;L↑&#xff09; 场景化指标选择响应时间优先吞吐量/容量优先平衡策略 概述 一个…

解决stylelint对deep报错

报错如图 在.stylelintrc.json的rules中配置 "selector-pseudo-class-no-unknown": [true,{"ignorePseudoClasses": ["deep"]} ]

VScode 中文符号出现黄色方框的解决方法

VScode 中文符号出现黄色方框的解决方法 我的vscode的python多行注释中会将中文字符用黄色方框框处&#xff1a; 只需要打开设置搜索unicode&#xff0c;然后将这一项的勾选取消掉就可以了&#xff1a; 取消之后的效果如下&#xff1a; 另一种情况&#xff1a;中文显示出现黄色…

大模型架构记录2

一 应用场景 1.1 prompt 示例 1.2 自己搭建一个UI界面&#xff0c;调用接口 可以选用不同的模型&#xff0c;需要对应的API KEY 二 Agent 使用 2.1 构建GPT

深度学习实战车辆目标跟踪与计数

本文采用YOLOv8作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv8以其高效的实时检测能力&#xff0c;在多个目标检测任务中展现出卓越性能。本研究针对车辆目标数据集进行训练和优化&#xff0c;该数据集包含丰富的车辆目标图像样本…

升级到Android Studio 2024.2.2 版本遇到的坑

一、上来就编译报错&#xff0c;大概率是因为选择了替换安装&#xff0c;本地配置文件出错 找到本地当前版本的配置文件&#xff0c;删掉&#xff0c;重启studio就好了&#xff1a; 1、打开终端 2、“cd /Users/用户名/Library/Application\ Support/Google” //到Google目录 …

Git - 补充工作中常用的一些命令

Git - 补充工作中常用的一些命令 1 一些场景1.1 场景11.2 场景21.3 场景31.4 场景41.5 场景51.6 场景61.7 场景71.8 场景81.9 场景91.10 场景101.11 场景111.12 场景121.13 场景131.14 场景141.15 场景15 2 git cherry-pick \<commit-hash\> 和 git checkout branch \-\-…

【网络安全工程】任务11:路由器配置与静态路由配置

目录 一、概念 二、路由器配置 三、配置静态路由CSDN 原创主页&#xff1a;不羁https://blog.csdn.net/2303_76492156?typeblog 一、概念 1、路由器的作用&#xff1a;通过路由表进行数据的转发。 2、交换机的作用&#xff1a;通过学习和识别 MAC 地址&#xff0c;依据 M…

如何用更少的内存训练你的PyTorch模型?深度学习GPU内存优化策略总结

在训练大规模深度学习模型时&#xff0c;GPU 内存往往成为关键瓶颈&#xff0c;尤其是面对大型语言模型&#xff08;LLM&#xff09;和视觉 Transformer 等现代架构时。由于大多数研究者和开发者难以获得配备海量 GPU 内存的高端计算集群&#xff0c;掌握高效的内存优化技术至关…

Dify+DeepSeek | Excel数据一键可视化(创建步骤案例)(echarts助手.yml)(文档表格转图表、根据表格绘制图表、Excel绘制图表)

Dify部署参考&#xff1a;Dify Rag部署并集成在线Deepseek教程&#xff08;Windows、部署Rag、安装Ragan安装、安装Dify安装、安装ollama安装&#xff09; DifyDeepSeek - Excel数据一键可视化&#xff08;创建步骤案例&#xff09;-DSL工程文件&#xff08;可直接导入&#x…

linux下ollama离线安装

一、离线安装包下载地址 直接下载地址&#xff1a; https://github.com/ollama/ollama/releases/tag/v0.5.12 网络爬取地址&#xff1a; MacOS https://ollama.com/download/Ollama-darwin.zip Linux curl -fsSL https://ollama.com/install.sh | sh Windows https://olla…

MAC 搭建Dify+DeepSeek-R1整合部署

在开始安装之前&#xff0c;我们需要确保系统满足以下基本要求&#xff1a; CPU至少2核心内存至少4GB&#xff08;建议8GB以上&#xff09;硬盘空间至少20GB&#xff08;为了后续扩展&#xff09;操作系统支持&#xff1a;Windows、macOS或LinuxDocker环境 1. dify的安装步骤…

OpenManus介绍及本地部署体验

1.OpenManus介绍 OpenManus&#xff0c;由 MetaGPT 团队精心打造的开源项目&#xff0c;于2025年3月发布。它致力于模仿并改进 Manus 这一封闭式商业 AI Agent 的核心功能&#xff0c;为用户提供无需邀请码、可本地化部署的智能体解决方案。换句话说&#xff0c;OpenManus 就像…

springboot011基于springboot的课程作业管理系统(源码+包运行+LW+技术指导)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得难了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等&#xff0c;你想解决的问题&#xff0c;今天…

swift -(5) 汇编分析结构体、类的内存布局

一、结构体 在 Swift 标准库中&#xff0c;绝大多数的公开类型都是结构体&#xff0c;而枚举和类只占很小一部分 比如Bool、 Int、 Double、 String、 Array、 Dictionary等常见类型都是结构体 ① struct Date { ② var year: Int ③ var month: Int ④ …

全域网络安全防御 健全网络安全防护体系

网络安全基本概念 网络安全&#xff08;Cyber Security&#xff09;是指网络系统的硬件、软件及其系统中的数据受到保护&#xff0c;不因偶然的或者恶意的原因而遭受到破坏、更改、泄露&#xff0c;系统连续可靠正常地运行&#xff0c;网络服务不中断&#xff0c;使网络处于稳…

记录小白使用 Cursor 开发第一个微信小程序(二):创建项目、编译、预览、发布(250308)

文章目录 记录小白使用 Cursor 开发第一个微信小程序&#xff08;二&#xff09;&#xff1a;创建项目、编译、预览、发布&#xff08;250308&#xff09;一、创建项目1.1 生成提示词1.2 生成代码 二、编译预览2.1 导入项目2.2 编译预览 三、发布3.1 在微信开发者工具进行上传3…