当前位置: 首页 > news >正文

【数据结构】- 栈

前言:

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


目录

                   前言:

栈的应用

括号匹配

逆波兰表达式

数制转换

栈的实现

栈的初始化,销毁

入栈和出栈操作

栈的大小

判空

访问栈顶元素

验证:

完整代码:

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;
}

总结:

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

相关文章:

  • 文件操作--文件包含漏洞
  • 如何让Steam下载速度解除封印?!
  • PyTorch线性代数操作详解:点积、矩阵乘法、范数与轴求和
  • 字符串转换整数(atoi)(8)
  • 在阿里云 Ubuntu 24.04 上部署 RabbitMQ:一篇实战指南
  • 【进阶】--函数栈帧的创建和销毁详解
  • Spring MVC 与 FreeMarker 整合
  • OpenGL-ES 学习(10) ---- OpenGL-ES Shader语言语法
  • 健康生活新主张:全方位养生指南
  • WPF嵌入webapi服务器,充当微服务角色
  • 人工智能的前世今生
  • IRF2.0IRF3.1
  • 基于开源链动2+1模式AI智能名片S2B2C商城小程序的个性化与小众化消费社群构建研究
  • Spring Boot 使用 WebMagic 爬虫框架入门
  • 利用无事务方式插入数据库解决并发插入问题
  • 深入解析 .NET Kestrel:高性能 Web 服务器的架构与最佳实践
  • 论文笔记——QWen2.5 VL
  • 二、HTML
  • curl详解
  • 从工厂到生活:算法 × 深度学习,正在改写自动化的底层逻辑
  • 上海与世界|黄菊与上海建设中国式全球城市
  • 如何反击右翼思潮、弥合社会分裂:加拿大大选镜鉴
  • 中国建设银行浙江省分行原党委书记、行长高强接受审查调查
  • 神舟十九号载人飞船因东风着陆场气象原因推迟返回
  • 牛市早报|国家发改委:将推出做好稳就业稳经济推动高质量发展若干举措
  • 北美票房|《罪人》遭媒体唱衰,好莱坞业内人士集体反击