
前言:
经过了几个月的漫长岁月,回头时年迈的小编发现,数据结构的内容还没有写博客,于是小编赶紧停下手头的活动,补上博客以洗清身上的罪孽
目录
概念:
栈(Stack)是一种先进后出(LIFO)的数据结构,元素的添加(入栈)和删除(出栈)仅在栈的顶部进行。出栈操作是从栈的顶部移除一个元素,并返回该元素的值。
++在栈里你如果要拿出一个数据,需要先拿出来后面放进去的;要拿出来前面放进去的,就得把后面放进去的全部拿出来++

栈的应用
括号匹配
栈可以用于检查括号是否匹配。算法的基本思想是遇到左括号时将其压入栈中,遇到右括号时从栈中弹出一个左括号。如果栈为空或最终栈不为空,则括号不匹配。
cpp
#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;
}
逆波兰表达式
栈可以用于计算逆波兰表达式。算法的基本思想是遇到数字时将其压入栈中,遇到运算符时从栈中弹出两个元素进行运算,并将结果压入栈中。
cpp
#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; // 默认表达式:输出 51
cout << reversePolishExpr("3,4,*,2,5,+,+") << endl; // 测试用例:输出 19
} catch (const exception& e) {
cerr << "错误: " << e.what() << endl;
}
return 0;
}
数制转换
栈可以用于将十进制数转换为其他进制。算法的基本思想是不断将N % 2的结果压入栈中,直到N为0,然后依次弹出栈中的元素组成结果。
cpp
#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在任何进制下都是0
if (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; // 1010
cout << "255 转 16进制: " << conversion(255, 16) << endl; // FF
cout << "100 转 8进制: " << conversion(100, 8) << endl; // 144
cout << "-42 转 2进制: " << conversion(-42) << endl; // -101010
cout << "0 转 16进制: " << conversion(0, 16) << endl; // 0
cout << "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为栈顶指针,指向下一个插入位置
cpp
typedef struct Stack {
STDataType* a; // 动态数组存储栈元素
int capacity; // 栈容量
int top; // 栈顶指针(指向下一个插入位置)
} ST;
栈的初始化,销毁
初始化:
断言指针有效,再动态分配空间,再看看是否分配成功,将初始容量设为4,栈顶指针初始化为0
销毁:
断言指针有效,释放动态数组,将指针置为空,容量和top也置为0
cpp
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,表示空栈
}
cpp
void STDestroy(ST* ps) {
assert(ps); // 确保栈指针有效
free(ps->a); // 释放动态数组内存
ps->a = NULL; // 指针置空避免野指针
ps->capacity = 0; // 容量归零
ps->top = 0; // 栈顶指针重置
}
入栈和出栈操作
入栈操作是将新元素放到栈顶,使其成为新的栈顶元素。出栈操作是从栈顶删除元素,使其相邻的元素成为新的栈顶元素。当栈中没有任何元素时称为空栈。
入栈:
检查栈容量,观察是否需要扩容,需要就扩容,不需要就将元素放入栈顶,然后将栈顶指针向上移动
出栈:
当栈不为空时才能出栈,直接将栈顶指针减一
cpp
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++; // 栈顶指针上移
}
cpp
void STPop(ST* ps) {
assert(ps);
assert(!STEmpty(ps)); // 确保栈非空
ps->top--; // 逻辑删除:仅下移栈顶指针
}
栈的大小
直接返回top,top指向下一个空闲位置,故等于元素数量
cpp
int STSize(ST* ps) //大小
{
assert(ps);
return ps->top;
}
判空
返回top,top为0表示没有元素
cpp
bool STEmpty(ST* ps)//判断是否为空
{
assert(ps);
return ps->top == 0;
}
访问栈顶元素
栈顶元素在top-1位置
cpp
STDataType STTop(ST* ps)//访问栈顶元素top
{
assert(ps);
assert(!STEmpty(ps));
return ps->a[ps->top-1];
}
验证:
cpp
ST st; // 声明栈变量(未初始化)
STInit(&st); // 初始化栈结构
作用 :调用 STInit
分配初始容量为4的动态数组,设置 top=0
cpp
printf("Stack empty? %s\n", STEmpty(&st) ? "true" : "false"); // 输出 true
printf("Stack size: %d\n", STSize(&st)); // 输出 0
作用:测试是否为空栈
cpp
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:
cpp
st.a → [1, ?, ?, ?]
top=1, capacity=4
输出: Push 1, Top: 1, Size: 1, Capacity: 4
Push 2-4:
cpp
st.a → [1, 2, 3, 4]
top=4, capacity=4
Push 5(触发扩容):
- 检测到
top == capacity
,调用realloc
扩容至8。
cpp
st.a → [1, 2, 3, 4, 5, ?, ?, ?] (新数组)
top=5, capacity=8
输出: Push 5, Top: 5, Size: 5, Capacity: 8
cpp
while (!STEmpty(&st)) {
printf("Pop %d, Size: %d\n", STTop(&st), STSize(&st));
STPop(&st);
}
作用:弹出栈里面的元素
将栈销毁
cpp
STDestroy(&st); // 释放动态数组内存,重置结构体

完整代码:
stack.h
cpp
#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
cpp
#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
cpp
#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"); // 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, 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;
}
总结:
本篇关于栈的讲解到这里就结束啦,后续小编会带来更多精彩实用的内容,对你有帮助的可以点个赞,欢迎各位队列交流学习