顺序栈:
一、数据结构定义
- 数据元素 DATATYPE
typedef struct person {
char name[32];
char sex;
int age;
int score;
} DATATYPE;
- 顺序栈结构 SeqStack
typedef struct list {
DATATYPE *head; // 栈空间首地址
int tlen; // 栈总容量(total length)
int top; // 栈顶指针(相当于当前元素计数)
} SeqStack;
二、核心操作接口
- 创建栈
SeqStack *CreateSeqStack(int size)
{
SeqStack *ss = malloc(sizeof(SeqStack));
if (NULL == ss)
{
perror("CreateSeqStack malloc error\n");
return NULL;
}
ss->head = malloc(sizeof(DATATYPE) * size);
if (NULL == ss)
{
perror("CreateSeqStack malloc2 error\n");
return NULL;
}
ss->tlen = size;
ss->top = 0;
return ss;
}
- 实现要点:
- 动态分配结构体内存
- 分配连续存储空间:
head = malloc(size * sizeof(DATATYPE))
- 初始化
tlen = size
,top = 0
- 销毁栈
int DestroySeqStack(SeqStack *ss)
{
if (NULL == ss)
{
fprintf(stderr, "DestroySeqStack pamter error\n");
return 1;
}
free(ss->head);
free(ss);
return 0;
}
内存释放顺序:
- 释放数据空间
free(ss->head)
- 释放控制结构
free(ss)
- 释放数据空间
- 入栈操作
int PushSeqStack(SeqStack *ss, DATATYPE *data) // add
{
if (NULL == ss || NULL == data || IsFullSeqStack(ss))
{
fprintf(stderr, "PushSeqStack pamter error\n");
return 1;
}
memcpy(&ss->head[ss->top], data, sizeof(DATATYPE));
ss->top++;
return 0;
}
- 实现流程:
if 栈未满:
复制数据到ss->head[top]位置
top++
返回成功
else:
返回失败
- 出栈操作
int PopSeqStack(SeqStack *ss)
{
if (NULL == ss)
{
fprintf(stderr, "PopSeqStack pamter error\n");
return 1;
}
ss->top--;
return 0;
}
- 实现逻辑:
if 栈非空:
top--
返回成功
else:
返回失败
- 判空操作
int IsEmpySeqStack(SeqStack *ss)
{
return 0 == ss->top;
}
- 判断条件:
top == 0
- 判满操作
int IsFullSeqStack(SeqStack *ss)
{
return ss->tlen == ss->top;
}
- 判断条件:
top == tlen
- 获取栈顶元素
DATATYPE *GetTopSeqStack(SeqStack *ss)
{
if (NULL == ss || IsEmpySeqStack(ss))
{
fprintf(stderr, "GetTopSeqStack pamter error\n");
return NULL;
}
return &ss->head[ss->top - 1];
}
- 注意点:
- 返回
ss->head[top-1]
的地址 - 需先判断栈是否为空
- 返回
- 获取栈大小
int GetSizeSeqStack(SeqStack *ss)
{
if (NULL == ss )
{
fprintf(stderr, "GetSizeSeqStack pamter error\n");
return 1;
}
return ss->top;
}
- 直接返回
top
值
三、存储结构示意图
栈底 → head[0]
head[1]
...
栈顶 → head[top-1] ← 当前有效元素
head[top] ← 下一个可用位置(当top<tlen时)
...
head[tlen-1]
四、时间复杂度分析
操作 | 时间复杂度 | 说明 |
---|---|---|
创建栈 | O(1) | 内存分配操作 |
销毁栈 | O(1) | 内存释放操作 |
入栈/出栈 | O(1) | 直接操作栈顶指针 |
获取栈顶元素 | O(1) | 随机访问特性 |
获取栈大小 | O(1) | 直接读取top值 |
五、优势与局限
-
优势:
- 随机访问特性,支持快速定位
- 内存连续,缓存命中率高
- 操作时间复杂度均为O(1)
-
局限:
- 固定容量,需要预先分配空间
- 扩容成本高(需要重新分配内存)
- 可能产生空间浪费(分配未使用的空间)
六、典型应用场景
- 函数调用栈的实现
- 表达式求值(中缀转后缀表达式)
- 括号匹配检测
- 浏览器的后退操作栈
- 撤销/重做功能实现
七、实现注意事项
-
内存管理:
- 创建时需分配两次内存(控制结构+数据空间)
- 销毁时需按相反顺序释放
-
临界条件处理:
- 入栈前必须检查栈是否已满
- 出栈前必须检查栈是否为空
- 获取栈顶元素前必须检查非空
八、实际应用
1.符号匹配
main 函数
#include "./SeqStack.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int do_check(char *buf, SeqStack *ss, int num)
{
int col = 1;
DATATYPE data;
while (*buf)
{
DATATYPE *tmp = NULL;
bzero(&data, sizeof(data));
int c = *buf;
switch (c)
{
case '(':
case '[':
case '{':
handle_left_bracket(c, num, col, &data, ss);
break;
case ')':
case ']':
case '}':
if (handle_right_bracket(c, num, col, ss))
{
return 1;
}
break;
}
buf++;
col++;
}
return 0;
}
// 封装左括号的处理逻辑
void handle_left_bracket(int c, int num, int col, DATATYPE *data, SeqStack *ss)
{
data->sym = c;
data->linenum = num;
data->colnum = col;
PushSeqStack(ss, data);
}
// 封装右括号的处理逻辑
int handle_right_bracket(int c, int num, int col, SeqStack *ss)
{
DATATYPE *tmp = GetTopSeqStack(ss);
if (tmp == NULL)
{
printf("read sym:%c ,line:%d col:%d\n", c, num, col);
return 1;
}
if ((c == ')' && tmp->sym == '(') ||
(c == ']' && tmp->sym == '[') ||
(c == '}' && tmp->sym == '{'))
{
PopSeqStack(ss);
return 0;
}
else
{
printf("read sym:%c ,line:%d col:%d or top sym:%c ,line:%d col:%d\n", c, num, col, tmp->sym, tmp->linenum, tmp->colnum);
return 1;
}
}
int main(int argc, char const *argv[])
{
SeqStack *ss = CreateSeqStack(5);
FILE *fp = fopen("./open.c", "r+");
if (NULL == fp)
{
perror("fopen");
return 1;
}
int num = 1;
int ret = 0;
while (1)
{
char buf[256] = {0};
if (NULL == fgets(buf, sizeof(buf), fp))
{
break;
}
ret = do_check(buf, ss, num);
if(1== ret)
{
DestroySeqStack(ss);
exit(1);
}
num++;
/* code */
}
if(IsEmpySeqStack(ss))
{
printf("file ok\n");
}
else{
DATATYPE *tmp = GetTopSeqStack(ss);
printf("top sym:%c ,line:%d col:%d\n", tmp->sym, tmp->linenum, tmp->colnum);
}
return 0;
}
2.四则运算(栈)
int applyOperator(int a, int b, char op)
{
switch (op)
{
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
return a / b;
default:
fprintf(stderr, "applyOperator error\n");
return 0;
}
}
int precedence(char op)
{
if (op == '+' || op == '-')
{
return 1;
}
if (op == '*' || op == '/')
{
return 2;
}
return 0;
}
int evaluateExpression(char *expression)
{
SeqStack *operand = CreateSeqStack(100);
SeqStack *opertor = CreateSeqStack(100);
int i = 0;
while (expression[i] != '\0')
{
char c = expression[i];
if (isdigit(c))
{
int num = 0;
while (isdigit(expression[i]))
{
num = num * 10 + (expression[i] - '0');
i++;
}
DATATYPE data;
data.op = '0';
data.num = num;
PushSeqStack(operand, &data);
}
else if (c == '+' || c == '-' || c == '*' || c == '/')
{
while (!IsEmpySeqStack(opertor) && precedence(c) <= precedence(GetTopSeqStack(opertor)->op))
{
DATATYPE opData = *GetTopSeqStack(opertor);
PopSeqStack(opertor);
DATATYPE bData = *GetTopSeqStack(operand);
PopSeqStack(operand);
DATATYPE aData = *GetTopSeqStack(operand);
PopSeqStack(operand);
int ret = applyOperator(aData.num, bData.num, opData.op);
DATATYPE retData;
retData.op = '0';
retData.num = ret;
PushSeqStack(operand, &retData);
}
DATATYPE data;
data.op = c;
PushSeqStack(opertor, &data);
i++;
}
else
{
i++;
}
}
// 处理运算符栈中剩余的运算符
while (!IsEmpySeqStack(opertor))
{
DATATYPE opData = *GetTopSeqStack(opertor);
PopSeqStack(opertor);
DATATYPE bData = *GetTopSeqStack(operand);
PopSeqStack(operand);
DATATYPE aData = *GetTopSeqStack(operand);
PopSeqStack(operand);
int ret = applyOperator(aData.num, bData.num, opData.op);
DATATYPE retData;
retData.op = '0';
retData.num = ret;
PushSeqStack(operand, &retData);
}
int result = GetTopSeqStack(operand)->num;
DestroySeqStack(operand);
DestroySeqStack(opertor);
return result;
}
链式栈(Linked Stack)
一、链式栈核心结构
1. 节点定义
// 数据元素类型
typedef struct person {
char name[32];
char sex;
int age;
int score;
} DATATYPE;
// 栈节点结构
typedef struct stacknode {
DATATYPE data; // 数据域
struct stacknode *next; // 指针域
} LinkStackNode;
// 栈管理结构
typedef struct list {
LinkStackNode *top; // 栈顶指针
int clen; // 当前元素个数
} LinkStack;
二、核心操作实现
1. 创建空栈
LinkStack*CreateLinkStack()
{
LinkStack* ls = malloc(sizeof(LinkStack));
if(NULL == ls)
{
perror("CreateLinkStack malloc error\n");
return NULL;
}
ls->top = NULL;
ls->clen = 0;
return ls;
}
2. 销毁栈
int DestroyLinkStack(LinkStack*ls)
{
if(NULL == ls )
{
fprintf(stderr,"DestroyLinkStack paramter error\n");
return 1;
}
while(!IsEmptyLinkStack(ls))
{
PopLinkStack(ls);
}
free(ls);
return 0;
}
三、栈操作实现
1. 入栈操作
int PushLinkStack(LinkStack*ls,DATATYPE*data)
{
if(NULL == ls || NULL == data)
{
fprintf(stderr,"PushLinkStack paramter error\n");
return 1;
}
LinkStackNode* newnode = malloc(sizeof(LinkStackNode));
if(NULL == newnode)
{
perror("PushLinkStack malloc error\n");
return 1;
}
memcpy(&newnode->data,data,sizeof(DATATYPE));
newnode->next = NULL;
if(IsEmptyLinkStack(ls))
{
ls->top = newnode;
}
else
{
newnode->next = ls->top;
ls->top = newnode;
}
ls->clen++;
return 0;
}
2. 出栈操作
int PopLinkStack(LinkStack*ls)
{
if(NULL == ls )
{
fprintf(stderr,"PopLinkStack paramter error\n");
return 1;
}
if(IsEmptyLinkStack(ls))
{
fprintf(stderr,"PopLinkStack empty\n");
return 1;
}
LinkStackNode* tmp = ls->top;
ls->top = ls->top->next;
free(tmp);
ls->clen--;
return 0;
}
四、辅助操作实现
1. 判空检测
int IsEmptyLinkStack(LinkStack* ls) {
return ls->clen == 0;
// 或 return ls->top == NULL;
}
2. 获取栈顶元素
DATATYPE*GetTopLinkStack(LinkStack*ls)
{
if(NULL == ls )
{
fprintf(stderr,"GetTopLinkStack paramter error\n");
return NULL;
}
if(IsEmptyLinkStack(ls))
{
fprintf(stderr,"GetTopLinkStack empty\n");
return NULL;
}
return &ls->top->data;
}
3. 获取栈大小
int GetSizeLinkStack(LinkStack* ls) {
return ls->clen;
}
五、链式栈 VS 顺序栈
对比维度 | 链式栈 | 顺序栈 |
---|---|---|
存储结构 | 离散内存节点 | 连续内存空间 |
容量限制 | 动态扩展 | 固定大小 |
内存开销 | 每个节点含指针(+8/16字节) | 无额外开销 |
入栈操作 | 动态分配内存 | 可能需扩容 |
缓存友好性 | 较差 | 优秀 |
实现复杂度 | 需处理指针操作 | 数组索引操作简单 |
六、典型应用场景
1. 函数调用栈
void funcA() {
// 保存当前上下文入栈
funcB();
// 弹出上下文
}
void funcB() {
// 新的栈帧入栈
}
2. 表达式求值
// 中缀转后缀表达式算法
while(输入队列不为空) {
读取token;
if(是数字) 加入输出队列;
else if(是运算符) {
while(栈顶运算符优先级 >= 当前运算符)
弹出栈顶到输出队列;
当前运算符入栈;
}
}
3. 括号匹配检测
bool isValid(char* s) {
LinkStack *ls = CreateLinkStack();
while(*s) {
if(*s == '(' || *s == '[' || *s == '{') {
PushLinkStack(ls, *s);
} else {
if(IsEmptyLinkStack(ls)) return false;
char top = GetTopLinkStack(ls);
if((*s == ')' && top != '(') ||
(*s == ']' && top != '[') ||
(*s == '}' && top != '{')) return false;
PopLinkStack(ls);
}
s++;
}
return IsEmptyLinkStack(ls);
}