数据结构:栈、队列详解篇

news2024/9/22 9:30:17

数据结构:栈、队列详解篇

  • 一、栈
    • (一)栈的概念
    • (二)栈的实现
      • 1、结构定义
      • 2、功能实现
        • (1)栈的初始化
        • (2)栈的销毁
        • (3)栈的扩容
        • (4)压栈
        • (5)出栈
        • (6)取栈顶元素、判空、栈的大小
    • (三)例题分析
      • 1、有效的括号
        • 题目分析
  • 二、队列
    • (一)队列的概念
    • (二)队列的实现
      • 1、结构定义
      • 2、功能实现
        • (1)队列结点生成
        • (2)队列初始化
        • (3)入队列
        • (4)出队列
        • (5)队列的销毁
        • (6)队列判空、取队列首元素、尾元素、数据个数
    • (三)例题分析
      • 1、用栈实现队列
        • 题目分析
      • 2、用队列实现栈
        • 题目分析
      • 3、设计循环队列
      • 结束语

一、栈

(一)栈的概念

在这里插入图片描述

⼀种特殊的线性表,其只允许在固定的⼀端进⾏插⼊和删除元素操作。进⾏数据插⼊和删除操作的⼀端称为栈顶,另⼀端称为栈底。栈中的数据元素遵守后进先出的原则。

(二)栈的实现

1、结构定义

在定义栈时,我们下面用数组实现栈,用到 typedef 可以使得栈的数据类型更广。

typedef int StackDataType;

typedef struct Stack {
	StackDataType* data;
	int capacity;
	int top;
}Stack;

2、功能实现

(1)栈的初始化

我们传进来的栈是 &(变量)的形式,因此这里不用使用 malloc 为栈开辟空间。

void StackInit(Stack* pS) {
	assert(pS);
	pS->data = NULL;
	pS->capacity = 0;
	pS->top = 0;
	return;
}
(2)栈的销毁

只用对 data 进行释放即可,因为栈本身没有进行动态空间的申请,就不用释放,但是要记得把变量的 capacity 和 top 重置为 0

void StackDestroy(Stack* pS) {
	assert(pS);
	if (pS->data) {
		free(pS->data);
		pS->data = NULL;
	}
	pS->capacity = pS->top = 0;
	return;
}
(3)栈的扩容

利用 realloc 进行扩容即可,三目运算符记得栈初始化大小为 0

void StackCheckCapacity(Stack* pS) {
	assert(pS);
	if (pS->capacity == pS->top) {
		int capacity = pS->capacity == 0 ? 4 : 2 * pS->capacity;
		StackDataType* s = (StackDataType*)realloc(pS->data, sizeof(StackDataType) * capacity);
		if (s == NULL) {
			perror("realloc fail!");
			exit(1);
		}
		pS->data = s;
		pS->capacity = capacity;
	}
	return;
}
(4)压栈

检查容量后压栈

void StackPush(Stack* pS, StackDataType x) {
	assert(pS);
	StackCheckCapacity(pS);
	pS->data[pS->top] = x;
	pS->top += 1;
	return;
}
(5)出栈

直接 -1 即可,空间还再只是不可非法访问。

void StackPop(Stack* pS) {
	assert(pS);
	pS->top -= 1;
	return;
}
(6)取栈顶元素、判空、栈的大小

这三个操作比较简单,但是记得断言一下指针是否为空,或者栈是否为空

bool StackEmpty(Stack* pS) {
	assert(pS);
	return pS->top == 0;
}

int StackSize(Stack* pS) {
	assert(pS);
	return pS->capacity;
}

//取栈顶元素
StackDataType StackTop(Stack* s) {
	assert(s);
	assert(!StackEmpty(s));
	return s->data[s->top - 1];
}

(三)例题分析

1、有效的括号

https://leetcode.cn/problems/valid-parentheses/description/在这里插入图片描述

题目分析

采用栈来解决,如果是左括号进栈,右括号出栈,最后判断栈是否为空,
下面直接用STL提供的stack来实现

class Solution {
public:
    bool isValid(string s) {
        stack<char> st;
        int i = 0;
        while (s[i]) {
            if (s[i] == '{' || s[i] == '[' || s[i] == '(') {
                st.push(s[i]);
            } else if (!st.empty() && (s[i] == '}' && st.top() == '{' ||
                        s[i] == ']' && st.top() == '[' ||
                        s[i] == ')' && st.top() == '(')) {
                st.pop();
            } else {
                return false;
            }
            i += 1;
        }
        if (st.empty())
            return true;
        return false;
    }
};

二、队列

(一)队列的概念

在这里插入图片描述

概念:只允许在⼀端进⾏插⼊数据操作,在另⼀端进⾏删除数据操作的特殊线性表,队列具有先进先出的性质
⼊队列:进⾏插⼊操作的⼀端称为队尾
出队列:进⾏删除操作的⼀端称为队头

(二)队列的实现

1、结构定义

我们下面用链表来实现队列,需要用到两个结构体定义结点结构和队列结构

typedef int QueueDataType;
typedef struct QueueNode {
	QueueDataType data;
	struct QueueNode* next;
}QueueNode;

typedef struct Queue {
	QueueNode* head;
	QueueNode* tail;
	int size;
}Queue;

2、功能实现

(1)队列结点生成

用队列的指针来接收动态内存申请的空间

QueueNode* BuyNode(QueueDataType data) {
	QueueNode* p = (QueueNode*)malloc(sizeof(QueueNode));
	if (p == NULL) {
		perror("malloc fail!");
		exit(1);
	}
	p->data = data;
	p->next = NULL;
	return p;
}
(2)队列初始化
void QueueInit(Queue* pQue) {
	assert(pQue);
	pQue->head = pQue->tail = NULL;
	pQue->size = 0;
	return;
}
(3)入队列

记得处理队列结点为空的情况

void QueuePush(Queue* pQue, QueueDataType data) {
	assert(pQue);
	QueueNode* node = BuyNode(data);
	if (pQue->head == NULL) {
		pQue->head = pQue->tail = node;
	}
	else {
		pQue->tail->next = node;
		pQue->tail = node;
	}
	pQue->size += 1;
	return;
}
(4)出队列

元素个数为 1 时,需要特殊处理。

void QueuePop(Queue* pQue) {
	assert(pQue);
	assert(!QueueEmpty(pQue));
	pQue->size -= 1;
	if (pQue->head == pQue->tail) {
		free(pQue->head);
		pQue->head = pQue->tail = NULL;
		return;
	}
	QueueNode* node = pQue->head;
	pQue->head = pQue->head->next;
	free(node);
	node = NULL;
	return;
}
(5)队列的销毁

遍历队列结点后销毁,然后记得将指针置空,将元素数量置为 0

void QueueDestroy(Queue* pQue) {
	assert(pQue);
	if (QueueEmpty(pQue)) return;
	QueueNode* p = pQue->head;
	while (p) {
		QueueNode* node = p;
		p = p->next;
		free(node);
	}
	pQue->head = pQue->tail = NULL;
	pQue->size = 0;
	return;
}
(6)队列判空、取队列首元素、尾元素、数据个数

代码难度不大,一样断言判断传入的数据是不是有效数据,以及+队列是否为空

bool QueueEmpty(Queue* pQue) {
	assert(pQue);
	return pQue->head == NULL && pQue->tail == NULL;
}


QueueDataType QueueFront(Queue* pQue) {
	assert(pQue);
	assert(!QueueEmpty(pQue));
	return pQue->head->data;
}

QueueDataType QueueBack(Queue* pQue) {
	assert(pQue);
	assert(!QueueEmpty(pQue));
	return pQue->tail->data;
}

int QueueSize(Queue* pQue) {
	assert(pQue);
	return pQue->size;
}

(三)例题分析

1、用栈实现队列

https://leetcode.cn/problems/implement-queue-using-stacks/description/
在这里插入图片描述

题目分析

创建两个栈 pushS 和 popS
push 操作直接往 pushS 里面压入元素
pop 操作弹出 popS 的栈顶元素。如果popS里面没有元素,就将pushS 的元素放进popS,再弹出栈顶元素

class MyQueue {
public:
    stack<int> pushS;
    stack<int> popS;

    MyQueue() {}
    
    void push(int x) {
        pushS.push(x);
    }
    
    int pop() {
        if(popS.empty()){
            while(pushS.size()){
                int top = pushS.top();
                popS.push(top);
                pushS.pop();
            }
        }
        int ret = popS.top();
        popS.pop();
        return ret;
    }
    
    int peek() {
        if(popS.empty()){
            while(pushS.size()){
                int top = pushS.top();
                popS.push(top);
                pushS.pop();
            }
        }
        return popS.top();
    }
    
    bool empty() {
        return popS.empty() && pushS.empty();
    }
};

小结:队列的两道题涉及栈和队列的相互转换,同时可以用来提升思维能力

2、用队列实现栈

https://leetcode.cn/problems/implement-stack-using-queues/description/
在这里插入图片描述

题目分析

用两个队列实现栈, 两个队列中,一个队列设置为空,另外一个放元素。
push 操作直接往有元素的队列放入元素即可。
pop 操作要先将有元素的队列 size - 1 个元素依次出队列,按照出队列顺序进入另外一个队列,然后弹出并返回最后一个元素即可。
top 操作直接返回有元素队列的最后一个元素。

class MyStack {
public:
    queue<int> q1;
    queue<int> q2;
    
    MyStack() {}
    
    void push(int x) {
        if (!q1.empty()) {
            q1.push(x);
        } else {
            q2.push(x);
        }
    }
    int pop() {
        queue<int> *emp = &q1;
        queue<int> *nemp = &q2;
        if (!q1.empty()) {
            emp = &q2, nemp = &q1;
        }
        int n = nemp->size() - 1;
        while (n--) {
            emp->push(nemp->front());
            nemp->pop();
        }
        int ret = nemp->front();
        nemp->pop();
        return ret;
    }

    int top() {
        queue<int> *emp = &q1;
        queue<int> *nemp = &q2;
        if (!q1.empty()) {
            emp = &q2, nemp = &q1;
        }
        return nemp->back();
    }

    bool empty() { return q1.empty() && q2.empty(); }
};

小结:这里不要错误的直接用引用取别名,emp 和 nemp 不然后面不可以修改

3、设计循环队列

https://leetcode.cn/problems/design-circular-queue/description/
在这里插入图片描述

class MyCircularQueue {
public:
    int* data;
    int capacity;
    int tail;
    int head;

    MyCircularQueue(int k) {
        data = (int*)malloc(sizeof(int) * (k + 1));
        capacity = k + 1;
        head = tail = 0;
    }
    
    bool enQueue(int value) {
        if(isFull()) return false;
        data[tail] = value;
        if((tail + 1) == capacity) tail = 0;
        else tail++;
        return true;
    }
    
    bool deQueue() {
        if(isEmpty()) return false;
        if((head + 1) == capacity) head = 0;
        else head++;
        return true;
    }
    
    int Front() {
        if(isEmpty()) return -1;
        return data[head];
    }
    
    int Rear() {
        if(isEmpty()) return -1;
        int ind = tail - 1;
        if(tail == 0) ind = capacity - 1;
        return data[ind];
    }
    
    bool isEmpty() {
        return head == tail;
    }
    
    bool isFull() {
        return ((tail + 1) % capacity) == head;
    }
};

小结:设计循环队列时,可以在申请 data 空间时多申请一份,这样就不用再创建另外的变量。另外,注意在 head tail 指针在 ++ 过程中,如果越界了要将位置移动到 0。

结束语

栈和队列的分析就到这里了,如果文章可以帮助到你,那真的十分有幸,记得动手支持支持小编哦!
在这里插入图片描述

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

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

相关文章

【大模型从入门到精通46】LLM部署运维(LLM Ops)使用Kubeflow Pipelines掌握LLM工作流3

这里写目录标题 功能概览函数定义实践示例&#xff1a;测试适当的拒绝最佳实践与建议适用于科学测验测试的修订函数科学测验测试函数定义执行与评估最佳实践与注意事项 功能概览 evaluate_request_refusal 函数模拟了系统应该基于预定义的标准拒绝生成测验的情景&#xff0c;这…

推荐9款AI论文写作推荐的论文指导!快速生成高质量初稿

在当前的学术写作领域&#xff0c;AI论文写作工具已经成为许多研究人员和学生的重要助手。这些工具不仅能够帮助用户快速生成高质量的论文初稿&#xff0c;还能在一定程度上简化学术写作流程&#xff0c;提高写作效率。以下是九款被广泛推荐的AI论文写作工具&#xff0c;它们各…

Docker续1:docker使用

一、打包传输 1.打包 [rootlocalhost ~]# systemctl start docker [rootlocalhost ~]# docker save -o centos.tar centos:latest [rootlocalhost ~]# ls anaconda-ks.cfg centos.tar 2.传输 [rootlocalhost ~]# scp centos.tar root192.168.1.100:/root 3.删除镜像 [r…

付费自习室管理小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商家管理&#xff0c;类型管理&#xff0c;自习室管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;自习室&#xff0c;我的 …

python-旋转木马(赛氪OJ)

[题目描述] 我们要做一个旋转木马&#xff01; 输入一个 nn 的字符矩阵&#xff0c;将其顺时针旋转 90 度后输出。输入格式&#xff1a; 输入共 n1 行。 第一行一个整数 n&#xff0c;表示矩阵大小。 接下来 n 行&#xff0c;每行一个长度为 n 的字符串&#xff0c;仅包含小写字…

Linux高性能服务器编程 总结索引 | 第2章:IP协议详解

IP头部信息 IP数据报的路由和转发 1、IP 服务的特点 1、IP 协议是 TCP/IP 协议族的动力&#xff0c;它为上层协议提供 无状态、无连接、不可靠的服务 2、无状态 是指 IP 通信双方不同步传输数据的状态信息&#xff0c;因此 所有 IP 数据报的发送、传输和接收都是相互独立、没…

【Linux应用编程实战】常见函数应用

介绍一些Linux应用编程实战遇到的&#xff0c;常见要用的函数&#xff0c;进行概况总结。 目录 main&#xff08;&#xff09; lseek&#xff08;&#xff09; poll&#xff08;&#xff09; struct pollfd 结构体返回值典例 mmap&#xff08;&#xff09; munmap&#xff08;…

kylin-麒麟操作系统-安装内存泄露补丁-以及kylin-kms-activation.service服务不断重启解决思路

文章目录 前言1. 问题现象1.1 使用journalctl命令查看更详细的日志信息 2. 解决思路2.1 思路一&#xff1a;2.2 思路二&#xff1a;2.3 合理的解法: 3. 扩展-修复内存泄露3.1 查看自己使用的镜像3.2 到麒麟官网下载相应的补丁包3.3 安装步骤3.4 重启kylin-kms-activation.servi…

python如何另起一行

python 字符串换行的三种方式&#xff1a; 第一种&#xff1a;三个单引号 print 我是一个程序员 我刚开始学习python 第二种&#xff1a;三个双引号 print """ 我是一个程序员 我刚开始学习python""" 第三种&#xff1a;\结尾 print "我是…

生成式AI,搜索赛道的又一个黄金十年

文&#xff5c;白 鸽 编&#xff5c;王一粟 随着生成式AI的发展&#xff0c;搜索引擎正在被重构&#xff0c;越来越多玩家开始布局AI搜索赛道。 一方面&#xff0c;传统搜索引擎/浏览器正借助AI技术的重构重新焕发生机&#xff0c;无论是移动端还是PC端&#xff0c;都在抢占…

GHA高质量原创文章是什么?

GHA文章是一种专为提高搜索引擎优化&#xff08;SEO&#xff09;效果而设计的高质量原创内容。GHA代表高质量&#xff0c;这些文章通过精心编写和策略布局&#xff0c;就是为了帮助网站迅速在Google等搜索引擎上获得排名&#xff0c;写一篇能在Google上获得高排名的文章&#x…

postman注入csrf

示例脚本 参数配置位置 必要参数 django项目仅需要设置domain即可&#xff0c;比如www.baidu.com,baidu.com尽量域名精确避免修改到其他域的参数 必须把这个domain添加到 cookies->Manage cookies ->Domains Allowlist 中&#xff0c;否则cookie的注入失败 代码 // 必…

P1516 青蛙的约会(exgcd)

一些前置知识&#xff1a; 1.扩展欧几里得算法&#xff1a; axbygcd(a,b) 方程一个可行的解&#xff08;x1,y1&#xff09;求法&#xff1a; int exgcd(int a,int b,int &x,int &y) {if(!b){x1,y0; return a;}int dexgcd(b,a%b,y,x);y-a/b*x;return d; }2.由axbygcd…

URP简洁的instance的写法

材质还是要开启enable instance&#xff0c;这是上一次的写法 https://dbbh666.blog.csdn.net/article/details/136644181 最近发现更适合我个人的习惯的写法 就是代码控制这个整个过程 C#代码是这样的&#xff0c;获取一个mesh&#xff0c;获取每个mesh的transform&#xff0c…

UE5 摄像机图像采集到材质 映射到 UI 和 物体表面

一.创建SceneCapture2D的组件 二.创建用于 映射的 贴图 三.将RenderTarget贴图放到SceneCapture2D的摄像机上Scene Capture的TextureTarget 四.这个时候的映射贴图&#xff0c;产生的材质可以直接。放到Plane上。 五&#xff0c;但是如果要用于UI,还需要更改SceneCapture2D的摄…

基于SpringBoot的在线答疑系统

你好呀&#xff0c;我是计算机专业毕业生&#xff0c;专注于在线教育平台的开发与实现。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java技术 Spring Boot框架 工具&#xff1a;IntelliJ IDEA、Navicat、Maven、Tomcat 系统展示 首页 个人中心…

【Python】简单的数据类型——int、float、bool、str

目录 1. 整数类型 int 2. 浮点数类型 float 3. 布尔类型 bool 4. 字符串 str 5. 格式化输出 6. 类型转换 6.1 隐式类型转换 6.2 显示类型转换 7. 标准输入 1. 整数类型 int a 10 print(type(a)) print(type(-2))<class int> <class int>测试整型能表示的…

docker私有云仓库Harbor部署及使用

文章目录 一、前置准备1、安装docker、docker-compose 二、安装harbor1、下载Harbor2、证书3、配置文件4、安装5、docker使用6、k8s使用&#xff08;1.28版本containerd&#xff09; 三、常用运维1、重启 一、前置准备 1、安装docker、docker-compose centos7安装与卸载docke…

2024年【道路运输企业安全生产管理人员】考试题库及道路运输企业安全生产管理人员考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年道路运输企业安全生产管理人员考试题库为正在备考道路运输企业安全生产管理人员操作证的学员准备的理论考试专题&#xff0c;每个月更新的道路运输企业安全生产管理人员考试试题祝您顺利通过道路运输企业安全生…

牛客NC313 两个数组的交集 C++

牛客NC313 两个数组的交集 C 思路&#x1f914;&#xff1a; 用哈希表存储第一个数组&#xff0c;再和第二个数组对比&#xff0c;对比成功就添加到新的数组中&#xff0c;之后将哈希表的该位置变为false&#xff0c;防止重复添加。这里数据范围仅有1000&#xff0c;所以我们可…