【数据结构】- 栈
前言:
经过了几个月的漫长岁月,回头时年迈的小编发现,数据结构的内容还没有写博客,于是小编赶紧停下手头的活动,补上博客以洗清身上的罪孽
目录
前言:
栈的应用
括号匹配
逆波兰表达式
数制转换
栈的实现
栈的初始化,销毁
入栈和出栈操作
栈的大小
判空
访问栈顶元素
验证:
完整代码:
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;
}
总结:
本篇关于栈的讲解到这里就结束啦,后续小编会带来更多精彩实用的内容,对你有帮助的可以点个赞,欢迎各位队列交流学习