【数据结构】- 栈

前言:

      经过了几个月的漫长岁月,回头时年迈的小编发现,数据结构的内容还没有写博客,于是小编赶紧停下手头的活动,补上博客以洗清身上的罪孽


目录

                   前言:

栈的应用

括号匹配

逆波兰表达式

数制转换

栈的实现

栈的初始化,销毁

入栈和出栈操作

栈的大小

判空

访问栈顶元素

验证:

完整代码:

stack.h

stack.c

test.c

总结:


概念:

      栈(Stack)是一种先进后出(LIFO)的数据结构,元素的添加(入栈)和删除(出栈)仅在栈的顶部进行。出栈操作是从栈的顶部移除一个元素,并返回该元素的值。

在栈里你如果要拿出一个数据,需要先拿出来后面放进去的;要拿出来前面放进去的,就得把后面放进去的全部拿出来

栈的应用
括号匹配

       栈可以用于检查括号是否匹配。算法的基本思想是遇到左括号时将其压入栈中,遇到右括号时从栈中弹出一个左括号。如果栈为空或最终栈不为空,则括号不匹配。

#include <iostream>
#include <stdexcept>
#include <string>using namespace std;class Stack {
private:char* data;int capacity;int top;public:Stack(int size) {capacity = size;data = new char[capacity];top = -1;}~Stack() {delete[] data;}void push(char c) {if (top >= capacity - 1) {throw runtime_error("栈已满");}data[++top] = c;}char pop() {if (isEmpty()) {throw runtime_error("栈为空");}return data[top--];}bool isEmpty() const {return top == -1;}
};bool balanced_parentheses(const string& parentheses) {Stack stack(parentheses.size());for (char parenthesis : parentheses) {if (parenthesis == '(') {stack.push(parenthesis);} else if (parenthesis == ')') {if (stack.isEmpty()) {return false;}stack.pop();} else {throw runtime_error("非法字符");}}return stack.isEmpty();
}int main() {string test1 = "((()))";    // 平衡string test2 = "(()))";     // 不平衡string test3 = "((a))";     // 含非法字符try {cout << boolalpha;cout << test1 << ": " << balanced_parentheses(test1) << endl;cout << test2 << ": " << balanced_parentheses(test2) << endl;cout << test3 << ": " << balanced_parentheses(test3) << endl;} catch (const runtime_error& e) {cerr << "错误: " << e.what() << endl;}return 0;
}
逆波兰表达式

       栈可以用于计算逆波兰表达式。算法的基本思想是遇到数字时将其压入栈中,遇到运算符时从栈中弹出两个元素进行运算,并将结果压入栈中。

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <stdexcept>
#include <unordered_map>
#include <functional>
#include <cmath> // for pow()using namespace std;int reversePolishExpr(const string& expr = "12,4,*,1,2,+,+") {vector<int> stack;unordered_map<string, function<int(int, int)>> ops = {{"^", [](int a, int b) { return static_cast<int>(pow(a, b)); }},{"*", [](int a, int b) { return a * b; }},{"/", [](int a, int b) { return a / b; }},  // 整数除法{"+", [](int a, int b) { return a + b; }},{"-", [](int a, int b) { return a - b; }}};istringstream iss(expr);string token;while (getline(iss, token, ',')) {if (ops.find(token) != ops.end()) {if (stack.size() < 2) {throw runtime_error("栈中的元素个数必须大于或等于两个");}int b = stack.back(); stack.pop_back();int a = stack.back(); stack.pop_back();stack.push_back(ops[token](a, b));} else {stack.push_back(stoi(token));}}if (stack.size() != 1) {throw runtime_error("表达式不完整或存在多余操作数");}return stack[0];
}int main() {try {cout << reversePolishExpr() << endl;  // 默认表达式:输出 51cout << reversePolishExpr("3,4,*,2,5,+,+") << endl;  // 测试用例:输出 19} catch (const exception& e) {cerr << "错误: " << e.what() << endl;}return 0;
}
数制转换

栈可以用于将十进制数转换为其他进制。算法的基本思想是不断将N % 2的结果压入栈中,直到N为0,然后依次弹出栈中的元素组成结果。

#include <iostream>
#include <stack>
#include <string>
#include <stdexcept>using namespace std;string conversion(int N, int K = 2) {// 检查进制范围是否合法(2-36)if (K < 2 || K > 36) {throw invalid_argument("进制K必须在2到36之间");}// 处理特殊情况:0在任何进制下都是0if (N == 0) {return "0";}// 处理负数情况bool isNegative = false;if (N < 0) {isNegative = true;N = -N;}const string digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";stack<int> Stack;// 计算各位数字while (N > 0) {Stack.push(N % K);N /= K;}// 构建结果字符串string res;if (isNegative) {res += "-";}while (!Stack.empty()) {int digit = Stack.top();Stack.pop();res += digits[digit];}return res;
}int main() {// 测试用例try {cout << "10 转 2进制: " << conversion(10) << endl;      // 1010cout << "255 转 16进制: " << conversion(255, 16) << endl; // FFcout << "100 转 8进制: " << conversion(100, 8) << endl;   // 144cout << "-42 转 2进制: " << conversion(-42) << endl;     // -101010cout << "0 转 16进制: " << conversion(0, 16) << endl;     // 0cout << "1024 转 36进制: " << conversion(1024, 36) << endl; // RS// cout << "10 转 1进制: " << conversion(10, 1) << endl;  // 会抛出异常} catch (const invalid_argument& e) {cerr << "错误: " << e.what() << endl;}return 0;
}
栈的实现

在开始前我们需要给栈定义一下,capacity为栈容量,top为栈顶指针,指向下一个插入位置

typedef struct Stack {STDataType* a;   // 动态数组存储栈元素int capacity;    // 栈容量int top;         // 栈顶指针(指向下一个插入位置)
} ST;
栈的初始化,销毁

初始化:

      断言指针有效,再动态分配空间,再看看是否分配成功,将初始容量设为4,栈顶指针初始化为0

销毁:

        断言指针有效,释放动态数组,将指针置为空,容量和top也置为0

void STInit(ST* ps) {assert(ps);  // 确保传入有效栈指针ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);  // 初始分配4个元素空间if (ps->a == NULL) {perror("malloc fail");  // 内存分配失败提示return;  // 返回后需由调用者处理错误}ps->capacity = 4;   // 设置初始容量ps->top = 0;        // 栈顶指针初始为0,表示空栈
}
void STDestroy(ST* ps) {assert(ps);  // 确保栈指针有效free(ps->a);    // 释放动态数组内存ps->a = NULL;   // 指针置空避免野指针ps->capacity = 0;  // 容量归零ps->top = 0;       // 栈顶指针重置
}
入栈和出栈操作

       入栈操作是将新元素放到栈顶,使其成为新的栈顶元素。出栈操作是从栈顶删除元素,使其相邻的元素成为新的栈顶元素。当栈中没有任何元素时称为空栈。

入栈:

       检查栈容量,观察是否需要扩容,需要就扩容,不需要就将元素放入栈顶,然后将栈顶指针向上移动

出栈:

        当栈不为空时才能出栈,直接将栈顶指针减一

void STPush(ST* ps, STDataType x) {assert(ps);  // 有效性检查// 检查是否需要扩容if (ps->top == ps->capacity) {STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);  // 尝试扩容为2倍if (tmp == NULL) {  // 扩容失败处理perror("realloc fail");return;  // 返回后栈保持原状态,但压栈失败}ps->a = tmp;                  // 更新数组指针ps->capacity *= 2;            // 更新容量}ps->a[ps->top] = x;  // 元素放入栈顶位置ps->top++;            // 栈顶指针上移
}
void STPop(ST* ps) {assert(ps);assert(!STEmpty(ps));  // 确保栈非空ps->top--;  // 逻辑删除:仅下移栈顶指针
}
栈的大小

 直接返回top,top指向下一个空闲位置,故等于元素数量

int STSize(ST* ps) //大小
{assert(ps);return ps->top;}
判空

返回top,top为0表示没有元素

bool STEmpty(ST* ps)//判断是否为空
{assert(ps);return ps->top == 0;
}
访问栈顶元素

栈顶元素在top-1位置

STDataType STTop(ST* ps)//访问栈顶元素top
{assert(ps);assert(!STEmpty(ps));return ps->a[ps->top-1];
}
验证:
ST st;              // 声明栈变量(未初始化)
STInit(&st);       // 初始化栈结构

作用:调用 STInit 分配初始容量为4的动态数组,设置 top=0

printf("Stack empty? %s\n", STEmpty(&st) ? "true" : "false");  // 输出 true
printf("Stack size: %d\n", STSize(&st));                      // 输出 0

作用:测试是否为空栈

for (int i = 1; i <= 5; i++) {STPush(&st, i);printf("Push %d, Top: %d, Size: %d, Capacity: %d\n", i, STTop(&st), STSize(&st), st.capacity);
}

作用:将数据入栈

Push 1

st.a → [1, ?, ?, ?]
top=1, capacity=4
输出: Push 1, Top: 1, Size: 1, Capacity: 4

Push 2-4

st.a → [1, 2, 3, 4]
top=4, capacity=4

Push 5(触发扩容)

  • 检测到 top == capacity,调用 realloc 扩容至8。

st.a → [1, 2, 3, 4, 5, ?, ?, ?]  (新数组)
top=5, capacity=8
输出: Push 5, Top: 5, Size: 5, Capacity: 8
while (!STEmpty(&st)) {printf("Pop %d, Size: %d\n", STTop(&st), STSize(&st));STPop(&st);
}

作用:弹出栈里面的元素

将栈销毁

STDestroy(&st);  // 释放动态数组内存,重置结构体

完整代码:
stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct stack {int* a;int top;int capacity;
}ST;
void STInit(ST* ps);//初始化
void STdestory(ST* ps);//销毁void STPush(ST* ps,STDataType x);//加 /插入
int STSize(ST* ps); //大小
bool STEmpty(ST* ps);//判断是否为空
void STPop(ST* ps);//删除/移出
STDataType STTop(ST* ps);//访问栈顶元素pop
stack.c
#include"stack.h"void STInit(ST* ps)
{assert(ps);ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);if (ps->a == NULL) {perror("malloc fail");return;}ps->capacity = 4;//ps->top=0;//0//top栈顶元素的下一个位置/-1,栈顶元素位置
}
void STDestroy(ST* ps)//销毁
{assert(ps);free(ps->a); ps->a = NULL;ps->capacity = 0;ps->top = 0;}void STPush(ST* ps, STDataType x)//加 /插入
{assert(ps);if (ps->top==ps->capacity){STDataType* tmp = (STDataType*)realloc(ps->a,sizeof(STDataType) * ps->capacity * 2);if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp;ps->capacity *= 2;}ps->a[ps->top] = x;ps->top++;}
int STSize(ST* ps) //大小
{assert(ps);return ps->top;}
bool STEmpty(ST* ps)//判断是否为空
{assert(ps);return ps->top == 0;
}
void STPop(ST* ps)//删除/移出
{assert(ps);assert(!STEmpty(ps));//为空时不能再减ps->top--;
}
STDataType STTop(ST* ps)//访问栈顶元素pop
{assert(ps);assert(!STEmpty(ps));return ps->a[ps->top-1];
}
test.c
#include"stack.h"
//int main()
//{
//	ST st;
//	STInit(&st);
//	STPush(&st,1);
//	STPush(&st,2);
//	STPush(&st,3);
//	STPush(&st,4);
//	STPush(&st,5);
//	
//	while (!STEmpty(&st))
//	{
//		printf("%d ", STTop(&st));
//		STPop(&st);
//	}
//	STdestory(&st);
//	return 0;
//}
int main() {ST st;STInit(&st);  // 初始化栈// 测试空栈printf("Stack empty? %s\n", STEmpty(&st) ? "true" : "false");  // trueprintf("Stack size: %d\n", STSize(&st));  // 0// 压入数据并测试扩容for (int i = 1; i <= 5; i++) {STPush(&st, i);printf("Push %d, Top: %d, Size: %d, Capacity: %d\n",i, STTop(&st), STSize(&st), st.capacity);}// 输出:// Push 1, Top: 1, Size: 1, Capacity: 4// Push 2, Top: 2, Size: 2, Capacity: 4// Push 3, Top: 3, Size: 3, Capacity: 4// Push 4, Top: 4, Size: 4, Capacity: 4// Push 5, Top: 5, Size: 5, Capacity: 8 (触发扩容)// 弹出元素while (!STEmpty(&st)) {printf("Pop %d, Size: %d\n", STTop(&st), STSize(&st));STPop(&st);}// 输出:// Pop 5, Size: 5// Pop 4, Size: 4// Pop 3, Size: 3// Pop 2, Size: 2// Pop 1, Size: 1// 测试空栈行为(触发断言)// STPop(&st);  // 若取消注释,程序会因断言失败终止STDestroy(&st);  // 销毁栈return 0;
}

总结:

       本篇关于栈的讲解到这里就结束啦,后续小编会带来更多精彩实用的内容,对你有帮助的可以点个赞,欢迎各位队列交流学习

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

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

相关文章

TDA4VM SDK J721E (RTOS/Linux) bootloaders梳理笔记

文章目录 1. 前言2. RTOS BootLoader2.1 引导模式2.2 启动序列2.2.1 流程框图2.2.2 Memory map2.3 镜像格式详解3. Linux BootLoader镜像格式详解启动流程参考1. 前言 TDA4VM的BootLoader包含两部分:RTOS的和Linux的。 2. RTOS BootLoader 这是在SoC上的所有内核运行FreeRTO…

Spring Boot + MyBatis-Plus 的现代开发模式

之前的Maven项目和本次需要的环境配置并不一样 之前使用的是&#xff1a; 传统的 MyBatis 框架&#xff08;非 Spring Boot 环境&#xff09; 手动管理 SqlSession 使用了 .xml 的 Mapper 映射文件 没有 Spring 容器管理&#xff08;没有 Service / RestController 等&…

【Quest开发】极简版!透视环境下抠出身体并能遮挡身体上的服装

前两天发了一个很复杂的版本&#xff0c;又鼓捣了一下发现完全没有必要。我之前的理解有点偏&#xff08;不是错误的但用法错了&#xff09;&#xff0c;但是有一些小伙伴收藏了&#xff0c;害怕里面的某些东西对谁有用&#xff0c;所以写了一篇新的&#xff0c;前两步配置环境…

vue 常见ui库对比(element、ant、antV等)

Element UI 1. 简介 Element UI 是一个基于 Vue 2 和 Vue 3 的企业级 UI 组件库&#xff0c;提供了丰富的组件和主题定制功能。官方网站&#xff1a;Element UI 2. 主要特点 丰富的组件&#xff1a;包括表单、表格、布局、导航、弹窗等多种组件。主题定制&#xff1a;支持主…

MATLAB画一把伞

% 伞的参数num_ribs 5; % 伞骨数量修改为5R 1; % 伞的半径height 0.5; % 伞的高度handle_length 2; % 伞柄长度semicircle_radius 0.26; % 伞柄末端半圆的半径% 生成伞叶网格theta linspace(0, 2*pi, 100);phi linspace(0, pi/2, 50);[Theta, Phi] meshgrid(theta, phi…

如何在 Go 中实现各种类型的链表?

链表是动态内存分配中最常见的数据结构之一。它由一组有限的元素组成&#xff0c;每个元素&#xff08;节点&#xff09;至少占用两块内存&#xff1a;一块用于存放数据&#xff0c;另一块用于存放指向下一个节点的指针。本文教程将说明在 Go 语言中如何借助指针和结构体类型来…

新一代机载相控阵雷达的发展

相控阵雷达以其优越的性能在军事领域中有着广阔的应用前景&#xff0c;但由于复杂的技术、昂贵的造价使其应用范围还存在一定的局限性。然而&#xff0c;国内外对相控阵技术的研究非常重视&#xff0c;并取得了丰硕的成果。 军用相控阵雷达主要分为陆基、海基和空基几种类型。 …

多数元素题解(LC:169)

169. 多数元素 核心思想&#xff08;Boyer-Moore 投票算法&#xff09;&#xff1a; 解题思路&#xff1a;可以使用 Boyer-Moore 投票算法、该算法的核心思想是&#xff1a; 维护一个候选元素和计数器、初始时计数器为 0。 遍历数组&#xff1a; 当计数器为 0 时、设置当前元…

数据库 AI 助手测评:Chat2DB、SQLFlow 等工具如何提升开发效率?

一、引言:数据库开发的 “效率革命” 正在发生 在某互联网金融公司的凌晨故障现场,资深 DBA 正满头大汗地排查一条执行超时的 SQL—— 该语句涉及 7 张核心业务表的复杂关联,因索引缺失导致全表扫描,最终引发交易系统阻塞。这类场景在传统数据库开发中屡见不鲜:据 Gartne…

【中间件】bthread效率为什么高?

bthread效率为什么更高&#xff1f; 1 基本概念 bthread是brpc中的用户态线程&#xff08;也可称为M:N线程库&#xff09;&#xff0c;目的是&#xff1a;提高程序的并发度&#xff0c;同时降低编码难度&#xff0c;在多核cpu上提供更好的scalability和cache locality。其采用…

DeepSeek V2:引入MLA机制与指令对齐

长上下文革命:Multi-Head Latent Attention(MLA)机制 传统 Transformer 的多头注意力需要缓存所有输入token的 Key 和 Value,这对长文本推理时的内存开销极为庞大。DeepSeek V2 针对这一难题提出了“Multi-Head Latent Attention”(MLA)机制。MLA 的核心思想是对多头注意…

Druid监控sql导致的内存溢出--内存分析工具MemoryAnalyzer(mat)

问题 druid监控sql在网页端显示&#xff0c;我的服务插入sql比较大&#xff0c;druid把执行过的sql保存在DruidDataSource类的成员变量JdbcDataSourceStat dataSourceStat&#xff1b; JdbcDataSourceStat类中的LinkedHashMap<String, JdbcSqlStat> sqlStatMap中&#…

《Python实战进阶》No45:性能分析工具 cProfile 与 line_profiler

Python实战进阶 No45&#xff1a;性能分析工具 cProfile 与 line_profiler 摘要 在AI模型开发中&#xff0c;代码性能直接影响训练效率和资源消耗。本节通过cProfile和line_profiler工具&#xff0c;实战演示如何定位Python代码中的性能瓶颈&#xff0c;并结合NumPy向量化操作…

计算机操作系统知识集合

主要来自小林coding 硬件结构 cpu位宽 如果用 32 位 CPU 去加和两个 64 位大小的数字&#xff0c;就需要把这 2 个 64 位的数字分成 2 个低位 32 位数字和 2 个高位 32 位数字来计算&#xff0c;先加个两个低位的 32 位数字&#xff0c;算出进位&#xff0c;然后加和两个高位…

电机常用易混淆概念说明(伺服、舵机、多轮)

1. 概述 基础动力需求 &#xff1a;普通电机&#xff08;如水泵、风扇&#xff09;。 高精度控制 &#xff1a;优先伺服系统或伺服电机&#xff08;如数控机床&#xff09;。 微型化场景 &#xff1a;舵机&#xff08;如遥控模型&#xff09;。 移动底盘 &#xff1a;单舵轮成…

进程与线程:04 内核线程

内核级线程概述 上一讲我们学习了用户级线程&#xff0c;了解了其切换和创建方式。用户级线程切换核心在于从一个栈变为两个栈&#xff0c;每个线程有自己的栈和线程控制块&#xff08;tcb&#xff09;&#xff0c;切换时先切换tcb再切换栈&#xff0c;创建时将切换的pc指针放…

信息系统项目管理师-软考高级(软考高项)​​​​​​​​​​​2025最新(六)

个人笔记整理---仅供参考 第六章项目管理概论 6.1PMBOK的发展 6.2项目基本要素 组织过程资产指的是项目上的&#xff0c;国产数据库的使用----安保和安全指的是环境因素 6.3项目经理的角色 6.4价值驱动的项目管理知识体系

[蓝桥杯 2023 国 Python B] 划分 Java

import java.util.*;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int[] arr new int[41];int sum 0;for (int i 1; i < 40; i) {arr[i] sc.nextInt();sum arr[i];}sc.close();int target sum / 2; // 最接近的两…

Redis05-进阶-主从

零、文章目录 Redis05-进阶-主从 1、搭建主从架构 &#xff08;1&#xff09;概述 单节点Redis的并发能力是有上限的&#xff0c;要进一步提高Redis的并发能力&#xff0c;就需要搭建主从集群&#xff0c;实现读写分离。 &#xff08;2&#xff09;集群概况 我们搭建的主从…

小结:ipsec-ike

IPSec 手动配置与自动配置&#xff08;IKE动态协商&#xff09; 手动配置IPSec 逻辑图 #mermaid-svg-eNMnNEwnoTjF8fkV {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-eNMnNEwnoTjF8fkV .error-icon{fill:#552222;}…