栈
概念:
栈(Stack)是一种先进后出(FILO,First in Last Out)的线性表,只允许在线性表的一端进行操作。
典型应用:
函数调用时,函数的上下文环境(返回地址,全局变量等),形参,是以栈这样的数据结构存储的。
基本操作:
1. 压栈
2. 出栈
3. 栈判空
4. 元素个数
5. 栈清空
6. 栈销毁
操作应用:
1. 进制转换
2. 括号匹配
3. 后缀表达式求值
4. 中缀转后缀
5. 迷宫问题求解
栈的实现方法:
方法1:静态顺序表
方法2:动态顺序表(高效)
方法3:只能头插头删的单链表
栈的定义(动态顺序表):
typedef struct Stack {
//以数组地址为栈底指针
DataType* _array;
//栈顶指针
int _top;
//栈的大小
unsigned int _capacity;
}*PStack;
初始化:
//初始化栈
void StackInit(PStack stack) {
stack->_array = (DataType*)malloc(sizeof(DataType)*Init_StackSize);
for (int i = 0; i < sizeof(stack->_array) / sizeof(stack->_array[0]); i++) {
stack->_array[i] = 0;
}
if (!stack->_array) {
exit(0);
}
stack->_top = 0;
stack->_capacity = Init_StackSize;
}
基本操作:
1. 压栈
//压栈
void StackPush(PStack stack, DataType data) {
assert(stack);
//判断栈是否已满
if (stack->_top >= stack->_capacity) {
//申请10个空间
stack->_array = (DataType*)realloc(stack->_array,
sizeof(DataType)*(stack->_capacity + 10));
//判断是否申请成功
if (!stack->_array) {
exit(0);
}
stack->_top = stack->_capacity;
stack->_capacity = stack->_capacity + 10;
}
*(stack->_array + stack->_top) = data;
stack->_top++;
}
2. 出栈(pop的数据放在data里面)
//出栈
int StackPop(PStack stack, DataType* data) {
assert(stack);
//判断栈是否为空
if (stack->_top == 0) {
return 0;
}
//先减一再取值
--stack->_top;
*data = *(stack->_array + stack->_top);
return 1;
}
3. 栈判空
//判断栈是否为空
int StackEmpty(PStack stack) {
assert(stack);
//为空返回1
return stack->_top == 0 ? 1 : 0;
}
4. 有效元素个数
//有效元素个数
int StackSize(PStack stack) {
assert(stack);
return stack->_top;
}
5. 栈清空
//清空栈,不进行物理删除
void ClearStack(PStack stack) {
assert(stack);
stack->_top = 0;
}
6. 栈销毁
//销毁栈
void dropStack(PStack stack) {
assert(stack);
int len = stack->_capacity;
for (int i = 0; i < len; i++) {
free(stack->_array);
stack->_array++;
}
stack->_array = NULL;
stack->_top = 0;
stack->_capacity = 0;
}
操作应用:
1. 进制转换
//二进制转10进制
int BinaryToTen(PStack stack, DataType* str) {
assert(stack);
unsigned int len = stack->_capacity;
char* p_str = str;
int sum = 0;
for (int i = 0; i < len; i++) {
if (*p_str == '\0')
break;
StackPush(stack, *p_str);
p_str++;
}
char binary = ' ';
for (int i = 0; i < len; i++) {
if (0 == StackPop(stack, &binary))
break;
sum += (binary - 48)*pow(2, i);
}
return sum;
}
2. 括号匹配
//括号匹配
void MatchBranckets(char* str) {
if (str == NULL)
return;
//初始化栈
PStack stack = (PStack)malloc(sizeof(struct Stack));
StackInit(stack);
char* p_str = str;
//用来保存弹出的字符
char c;
while (*p_str) {
if (*p_str == '{' || *p_str == '(' || *p_str == '[') {
StackPush(stack, *p_str);
}
if (*p_str == '}' || *p_str == ')' || *p_str == ']') {
StackPop(stack, &c);
if (c == '{' && *p_str == '}');
else if (c == '(' && *p_str == ')');
else if (c == '[' && *p_str == ']');
else {
printf("匹配失败\n");
return;
}
}
p_str++;
}
//遍历完之后,栈中还有括号,代表匹配失败
if (StackPop(stack, &c))
{
printf("匹配失败\n");
return;
}
printf("匹配成功\n");
}
3. 后缀表达式求值
//根据操作符的不同进行不同的计算
int caculate(int first, int second, char operator) {
if (operator == '+') {
return first + second;
}
else if (operator == '-') {
return first - second;
}
else if (operator == '*') {
return first * second;
}
else if (operator == '/') {
return first / second;
}
}
//后缀表达式求值
int caculateNifixExpression(char* str) {
//用来放整形操作数
PStack operator = (PStack)malloc(sizeof(struct Stack));
StackInit(operator);
if (str == NULL)
return;
char* p_str = str;
char* p_preStr = str;
int StackPopData = 0;
//弹出的第一个操作数,右操作数
int first = 0;
//弹出的第二个操作数, 左操作数
int second = 0;
while (*p_str) {
if (*p_str == ' ') {
//空格前是数字,压栈
if (*p_preStr >= '0' && *p_preStr <= '9')
StackPush(operator, atoi(p_preStr));
p_preStr = p_str + 1;
}
if (*p_str == '+' || *p_str == '-' || *p_str == '*' || *p_str == '/') {
StackPop(operator, &StackPopData);
second = StackPopData;
StackPop(operator, &StackPopData);
first = StackPopData;
//计算结果
StackPopData = caculate(first, second, *p_str);
//结果入栈
StackPush(operator, StackPopData);
}
p_str++;
}
StackPop(operator, &StackPopData);
printf("计算结果为%d\n", StackPopData);
return StackPopData;
}
4.中缀表达式转后缀表达式
思路:
(1).初始化两个栈(一个用来运算operand,一个用来存放结果operand)
(2).扫描字符串
- 遇到操作数入栈
- 遇到'('直接入栈
- 遇到')',一直出栈,直到遇到(,将左括号出栈
- 遇到其他操作符,先将这个操作符和用来运算的栈(operator)的栈顶元素比较,如果栈顶运算符的优先级较高,将栈顶运算符入存放结果的栈(operand),并将当前运算符入(operator)栈。
//中缀转后缀
char* SuffixToNifix(char* str) {
//操作数栈
PStack operand = (PStack)malloc(sizeof(struct Stack));
StackInit(operand);
//操作符栈
PStack operator = (PStack)malloc(sizeof(struct Stack));
StackInit(operator);
char buffer;
while (*str) {
//判断是操作数还是操作符
if (*str >= '0' && *str <= '9') {
//操作数入栈
StackPush(operand, *str);
if(*(str + 1) < '0' || *(str + 1) > '9')
if (StackTop(operand) != 0)
StackPush(operand, 0);
}
else {
/*if (StackTop(operand) != 0)
StackPush(operand, 0);*/
//扫描到操作符
//判断操作符栈是否为空,为空直接入栈
if (1 == StackEmpty(operator)) {
StackPush(operator, *str);
}
else if ('(' == *str) {
//遇到左括号,直接入栈,左括号的优先级最高
StackPush(operator, *str);
}
else if (')' == *str) {
//遇到右括号,一直出栈,并压栈到操作数栈,直到把括号弹出
while (1) {
StackPop(operator, &buffer);
if (buffer == '(')
break;
StackPush(operand, buffer);
/*if (StackTop(operand) != 0)
StackPush(operand, 0);*/
}
}
else {
//非空,则比较当前栈顶运算符和遇到的运算符的优先级
char operatorTop = StackTop(operator);
if (cmpPriority(operatorTop, *str) > 0 && operatorTop != '(') {
//栈顶运算符优先级较高,弹出栈顶运算符,并压到操作数栈
StackPop(operator, &buffer);
StackPush(operand, buffer);
StackPush(operator, *str);
}
else {
//栈顶运算符优先级较低,当前运算符直接入栈
StackPush(operator, *str);
}
}
}
str++;
}
char buf;
while (StackPop(operator, &buf)) {
StackPush(operand, buf);
}
while (StackPop(operand, &buf)) {
StackPush(operator, buf);
}
while (StackPop(operator, &buf)) {
printf("%c", buf);
}
printf("\n");
return NULL;
}
5. 迷宫问题
思路:
非递归:
(1)入口点压栈。
(2)以左上右下的顺序判断周围有哪个坐标可以到达(数组的值设置为1)。
(3)遇到可以到达的坐标就把当前坐标设置为这个坐标,将当前坐标设置为不可达(数组的值设置为2),并把这个坐标压栈
(4)重复(2)(3)步。
(5)遇到了死胡同(周围都不可达),栈顶坐标出栈,并将弹出的坐标设置为当前坐标。
(6)判断是否到达了起点(无出口),判断是否到达了边界位置(非起点),到达边界跳出。未到达边界。
(7)重复(2)(3)步
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#define INIT_STACKSIZE 100
#define WIDTH 10
//设置当前坐标的状态,将当前坐标入栈
#define SET_WENTSTATE_AND_PUSHSTACK \
map[y][x] = 2;\
loc._x = x;\
loc._y = y;\
StackPush(s, loc);\
continue
typedef struct Location {
//坐标
int _x;
int _y;
}Location;
typedef Location DataType;
typedef struct Stack {
DataType* _array;
int _top;
//最大容量
int _capacity;
}*PStack, Stack;
void StackInit(PStack s) {
s->_array = (PStack)malloc(sizeof(Stack)*INIT_STACKSIZE);
if (s->_array == NULL)
exit(0);
s->_capacity = INIT_STACKSIZE;
s->_top = 0;
}
void StackPush(PStack s, DataType loc) {
assert(s);
if (s->_top >= s->_capacity) {
s->_array = (PStack)realloc(s->_array, ((s->_capacity)+10)*sizeof(Stack));
if (s->_array == NULL)
exit(0);
s->_capacity = s->_capacity + 10;
}
*(s->_array + s->_top) = loc;
s->_top++;
}
int StackPop(PStack s, DataType* loc) {
assert(s);
if (!s->_top) {
return 0;
}
--s->_top;
*loc = *(s->_array + s->_top);
return 1;
}
DataType StackTop(PStack s) {
return *(s->_array + s->_top);
}
//判断是否到达边界
int isBorder(int x, int y) {
if (x == WIDTH - 1 || x == 0 || y == 0 || y == WIDTH - 1)
return 1;
return 0;
}
//判断是否回到了起点
int isStart(int start_x, int start_y, int x, int y) {
if (start_x == x && start_y == y)
return 1;
return 0;
}
//打印地图
void showMap(int map[WIDTH][WIDTH]) {
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < WIDTH; j++) {
if (map[i][j] == 0) {
printf("█");
}
else if(map[i][j] == 1){
printf(" ");
}
else {
printf(" *");
}
//printf("%d ", map[i][j]);
}
printf("\n");
}
}
void maze(int map[WIDTH][WIDTH], int start_x, int start_y) {
PStack s = (PStack)malloc(sizeof(Stack));
StackInit(s);
//入口
int x = start_x;
int y = start_y;
//将入口位置设置为已走过的状态
map[y][x] = 2;
//将当前坐标压栈,
showMap(map);
Location loc = { x, y };
StackPush(s, loc);
while (1) {
int oldx = x;
int oldy = y;
//左
if (map[y][x - 1] == 1) {
--x;
SET_WENTSTATE_AND_PUSHSTACK;//宏:入栈和赋值当前坐标操作。
}
//上
if (map[y - 1][x] == 1) {
--y;
SET_WENTSTATE_AND_PUSHSTACK;
}
//右
if (map[y][x + 1] == 1) {
++x;
SET_WENTSTATE_AND_PUSHSTACK;
}
//下
if (map[y + 1][x] == 1) {
++y;
SET_WENTSTATE_AND_PUSHSTACK;
}
Location loc1;
if (!StackPop(s, &loc1)) {
if (isStart(start_x, start_y, oldx, oldy)) {
printf("迷宫无出口\n");
break;
}
break;
}
//判断是否到达边界
if (isBorder(x, y)) {
//判断是否遇到起始位置
showMap(map);
break;
}
//遇到分叉点的处理,因为第二次到达分叉点的时候分叉点已经弹出。所以要求出分叉点的坐标
if (oldx + 2 == loc1._x || oldy + 2 == loc1._y ||
oldx - 2 == loc1._x || oldy - 2 == loc1._y
) {
x = (oldx + loc1._x) / 2;
y = (oldy + loc1._y) / 2;
StackPush(s, loc1);
}
else {
x = loc1._x;
y = loc1._y;
}
}
}
//迷宫问题递归解法
int ENDX = 0;
int ENDY = 8;
int mazeRecursion(int map[][WIDTH], int x, int y) {
map[y][x] = 2;
int isEnd = 0;//判断是不是终点
if (ENDX == x && ENDY == y) {
isEnd = 1;
}
//左
if (isEnd != 1 && x >= 0 && map[y][x - 1] == 1) {
if(mazeRecursion(map, x - 1, y) == 1)
return 1;
}
//上
if (isEnd != 1 && y >= 0 && map[y - 1][x] == 1) {
if (mazeRecursion(map, x, y - 1) == 1)
return 1;
}
//右
if (isEnd != 1 && x < WIDTH && map[y][x + 1] == 1) {
if (mazeRecursion(map, x + 1, y) == 1)
return 1;
}
//下
if (isEnd != 1 && y < WIDTH && map[y + 1][x] == 1) {
if (mazeRecursion(map, x, y + 1) == 1)
return 1;
}
//未到终点,但是没有路可以走了。
if (isEnd == 0) {
map[y][x] = 1;
}
//未到终点返回0。
return isEnd;
}
int main() {
int map[WIDTH][WIDTH] = {
{ 0,0,0,0,0,0,1,0,0,0 },
{ 0,0,1,1,1,0,1,0,1,0 },
{ 0,0,1,0,1,0,1,0,1,0 },
{ 0,0,1,0,1,0,1,0,1,0 },
{ 0,0,1,0,0,0,1,0,1,0 },
{ 0,0,1,1,1,0,1,0,1,0 },
{ 0,0,1,0,1,0,1,1,1,0 },
{ 0,0,1,0,1,0,0,0,1,0 },
{ 1,1,1,0,1,1,1,1,1,0 },
{ 0,0,0,0,0,0,0,0,0,0 }
};
printf("非递归:\n");
maze(map, 6, 0);
/*printf("递归:\n");
mazeRecursion(map, 6, 0);
showMap(map);*/
return 0;
}
运行结果:
详细代码在:Niceug