1. 深入理解栈
2. 栈概念
栈是限定仅在表尾进行插入和删除操作线性表
栈顶(top):允许插入、删除的一端,一般会有一个top指针指向当前的栈顶元素 表尾
栈底(bottom):不允许插入、删除的一端 表头
空栈:没有数据
入栈:栈的插入
出栈:栈的删除
特点:后进先出 last in first out(LIFO)
3. 顺序栈(数组)
1. 栈空:s->top == -1;
2. 栈满:s->top == MAXSIZE - 1
3. 元素进栈:假设栈顶要去放一个元素b
s->top++;
s->data[s->top] = b;4. 元素出栈:假设栈顶要出栈元素b ElemType e;
e = s->data[s->top];
top--; // 将栈顶指针减1SequenceStack.h
#ifndef __SEQUENCESTACK_H__ #define __SEQUENCESTACK_H__ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAXSIZE 1024 typedef int SElemType; // 顺序栈的数据类型 typedef struct { // SElemType data[MAXSIZE]; SElemType *data; // data会指向一段地址连续的空间,来存储栈中的每个元素 int top; // 栈顶元素的下标 -1表示空栈,栈元素的个数 = top + 1 } SqStack; extern SqStack* init_stack(); // 初始化一个栈 extern SqStack* destroy_stack(SqStack *s); // 销毁一个栈 extern void clear_stack(SqStack *s); // 清空一个栈 extern bool StackEmpty(SqStack *s); // 判断栈是否为 extern void print_stack(SqStack *s); // 打印栈中所有元素 extern int stack_length(SqStack *s); // 返回栈的数据结点的个数(栈的长度) extern bool GetTop(SqStack *s, SElemType *d); // 获取栈顶元素的值,但是不出栈 extern bool Pop(SqStack *s, SElemType *d); // 出栈,把栈顶元素出栈,并返回 extern bool Push(SqStack *s, SElemType d); // 入栈(压栈),把数据存入栈中 #endif
SequenceStack.c
#include "SequenceStack.h" // 初始化一个栈 SqStack* init_stack() { SqStack *s = malloc(sizeof(*s)); s->data = malloc(sizeof(SElemType) * MAXSIZE); s->top = -1; // 表示空栈 return s; } // 销毁一个栈 SqStack* destroy_stack(SqStack *s) { if (s == NULL) { return NULL; } if (s->data) { // if (s->data != NULL) free(s->data); } free(s); s = NULL; return s; } // 清空一个栈 void clear_stack(SqStack *s) { if (s == NULL || s->data == NULL || s->top == -1) { return; } s->top = -1; } // 判断栈是否为空 bool StackEmpty(SqStack *s) { // s不存在 || 栈为空 if (s == NULL || s->data == NULL || s->top == -1) { return true; } return false; } // 打印栈中所有元素 void print_stack(SqStack *s) { if (StackEmpty(s)) { return; } for (int i = 0; i <= s->top; i++) { printf("%d ", s->data[i]); } putchar('\n'); } // 返回栈的数据结点的个数(栈的长度) int stack_length(SqStack *s) { if (s == NULL || s->data == NULL) { return 0; } return s->top + 1; } // 获取栈顶元素的值,但是不出栈 bool GetTop(SqStack *s, SElemType *d) { // 不能获取栈顶元素 if (s == NULL || s->data == NULL || s->top == -1) { return false; } // 能获取栈顶元素 *d = s->data[s->top]; return true; } // 出栈,把栈顶元素出栈,并返回 bool Pop(SqStack *s, SElemType *d) { // 不能出栈 if (s == NULL || s->data == NULL || s->top == -1) { return false; } // 出栈 获得元素 *d = s->data[s->top--]; return true; } // 入栈(压栈),把数据存入栈中 bool Push(SqStack *s, SElemType d) { // 不能入栈 if (s == NULL || s->data == NULL || s->top == MAXSIZE - 1) { return false; } // 入栈 s->data[++s->top] = d; // ->优先级高于++ return true; }
main.c
#include "SequenceStack.h" int main(int argc, char *argv[]) { SqStack *s = init_stack(); // 初始化一个栈 SElemType d; while (1) { scanf("%d", &d); if (d == 0) { break; } Push(s, d); // 入栈 } printf("length: %d\n", stack_length(s)); // 输出栈长度 print_stack(s); // 打印栈中所有元素 if (GetTop(s, &d)) { // 获取栈顶元素,但是不出栈 printf("top = %d\n", d); } while (!StackEmpty(s)) { if (Pop(s, &d)) { // 出栈 printf("出栈:%d\n", d); } printf("length: %d\n", stack_length(s)); // 输出栈长度 print_stack(s); // 打印栈中所有元素 } clear_stack(s); // 清空栈 if (StackEmpty(s)) { // 判断栈是否为空 printf("栈空\n"); } s = destroy_stack(s); // 摧毁栈 return 0; }
4. 单链栈
LinkedStack.h
#ifndef __LINKEDSTACK_H__ #define __LINKEDSTACK_H__ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> typedef int SElemType; // 数据结点 struct node { SElemType data; // 数据域 struct node *next; // 指针域 }; // 栈的数据类型 typedef struct { struct node *bottom; // 指向栈的第一个数据结点 struct node *top; // 指向栈的最后一个数据结点 int NodeNum; // 栈的数据结点个数 } LinkedStack; extern LinkedStack* init_stack(); // 初始化一个栈 extern void clear_stack(LinkedStack *ls); // 清空一个栈 void print_stack(LinkedStack *ls); // 打印栈中所有元素 extern LinkedStack* destroy_stack(LinkedStack *ls); // 销毁一个栈 extern bool StackEmpty(LinkedStack *ls); // 判断栈是否为空 extern int stack_length(LinkedStack *ls); // 返回栈的数据结点的个数(栈的长度) extern bool GetTop(LinkedStack *ls, SElemType *e); // 获取栈顶元素,但是不出栈 extern bool Pop(LinkedStack *ls, SElemType *e); // 出栈 extern bool Push(LinkedStack *ls, SElemType d); // 入栈 #endif
LinkedStack.c
#include "LinkedStack.h" // 初始化一个栈 LinkedStack* init_stack() { LinkedStack *ls = malloc(sizeof(*ls)); ls->bottom = NULL; // 指向栈的第一个数据结点 ls->top = NULL; // 指向栈的最后一个数据结点 ls->NodeNum = 0; // 栈的数据结点个数 return ls; } // 清空一个栈 void clear_stack(LinkedStack *ls) { // 栈不存在 || 栈为空 if (ls == NULL || ls->NodeNum == 0) { return; } // 尾删 while (ls->top) { struct node *p = ls->bottom; struct node *pre = NULL; // 找到被删结点的前一个结点 while (p != ls->top) { pre = p; p = p->next; } if (p == ls->bottom) { // 只有一个结点的情况 free(p); ls->bottom = NULL; ls->top = NULL; ls->NodeNum = 0; } else { ls->top = pre; ls->top->next = NULL; free(p); } } } // 打印栈中所有元素 void print_stack(LinkedStack *ls) { // 异常处理 if (ls == NULL || ls->NodeNum == 0) { return; } struct node *p = ls->bottom; while (p) { printf("%d ", p->data); p = p->next; } putchar('\n'); } // 销毁一个栈 LinkedStack* destroy_stack(LinkedStack *ls) { if (ls == NULL) { return ls; } // 清空栈 clear_stack(ls); // 释放头结点 free(ls); ls = NULL; return ls; } // 判断栈是否为空 空:返回true 不为空:返回false bool StackEmpty(LinkedStack *ls) { if (ls == NULL || ls->NodeNum == 0) { return true; } return false; } // 返回栈的数据结点的个数(栈的长度) int stack_length(LinkedStack *ls) { if (ls) { return ls->NodeNum; } return 0; // 出错 } // 获取栈顶元素,但是不出栈 bool GetTop(LinkedStack *ls, SElemType *e) { // 不能获取栈顶元素 if (ls == NULL || ls->NodeNum == 0) { return false; } // 能获取栈顶元素 *e = ls->top->data; return true; } // 出栈 bool Pop(LinkedStack *ls, SElemType *e) { // 异常处理 if (ls == NULL || StackEmpty(ls)) { return false; } // 拆除尾结点 struct node *p = ls->bottom; // 遍历指针 struct node *pre = NULL; // 遍历指针的前驱结点 while (p != ls->top) { pre = p; p = p->next; } *e = p->data; //获取栈顶元素 ls->top = pre; if (ls->top) { ls->top->next = NULL; } free(p); ls->NodeNum--; // 如果为空栈,要记得更新bottom if (ls->NodeNum == 0) { ls->bottom = NULL; } return true; } // 入栈 bool Push(LinkedStack *ls, SElemType d) { // 异常处理 if (ls == NULL) { return false; } // 创建新结点并赋值 struct node *pnew = malloc(sizeof(*pnew)); pnew->data = d; pnew->next = NULL; if (ls->top == NULL) { // 从无到有 ls->top = pnew; ls->bottom = pnew; } else { // 由少到多 尾插 ls->top->next = pnew; ls->top = pnew; } ls->NodeNum++; return true; }
main.c
#include "LinkedStack.h" int main(int argc, char *argv[]) { LinkedStack *ls = init_stack(); // 初始化一个栈 SElemType d; while (1) { scanf("%d", &d); if (d == 0) { break; } Push(ls, d); // 入栈 } printf("length: %d\n", stack_length(ls)); // 输出栈长度 print_stack(ls); // 打印栈中所有元素 if (GetTop(ls, &d)) { // 获取栈顶元素,但是不出栈 printf("top = %d\n", d); } while (!StackEmpty(ls)) { if (Pop(ls, &d)) { // 出栈 printf("出栈:%d\n", d); } printf("length: %d\n", stack_length(ls)); // 输出栈长度 print_stack(ls); // 打印栈中所有元素 } clear_stack(ls); // 清空栈 if (StackEmpty(ls)) { // 判断栈是否为空 printf("栈空\n"); } ls = destroy_stack(ls); // 摧毁栈 return 0; }
5. 中缀表达式转后缀表达式
中缀表达式(正常表达式):a+b*c+(d*e+f)*g
转成后缀表达式(逆波兰式):abc*+de*f+g*+ (从左到右)
转成前缀表达式(波兰式):++a*bc*+*defg (从右到左,最后倒序打印)
中缀表达式(正常表达式):(a+b)*c+d-(e+g)*h
转成后缀表达式(逆波兰式):ab+c*d+eg+h*- (从左到右)
转成前缀表达式(波兰式): -+*+abcd*+egh (从右到左,最后倒序打印)
用到栈、步骤
1. 如果遇到操作数,我们就直接输出
2. 遇到左括号,将其入栈;遇到右括号,则将栈中元素依次出栈直到遇到左括号为止注意:左括号只是出栈并不输出
3. 如果遇到任何的运算符,从栈中出栈并且输出直到遇到优先级(中缀表达式的运算顺序)更低的为止(或栈为空)。才将进行遇到的运算符入栈的操作 (前面的+、-比后面的+、-优先级高,前面的*、/比后面的*、/优先级高) 只有在遇到右括号的情况下,我们才去出栈左括号,其他的情况下,左括号不会出栈4. 如果我们读到了中缀表达式的末尾,则将栈中的所有元素依次出栈
具体的过程
1. 首先读到a,直接输出
2. 读到+ 入栈
3. 读到b 直接输出
4. 读到* 因为栈顶元素+优先级比*低 所以将*入栈
5. 读到c 直接输出
6. 读到+ 因为栈顶元素*的优先级比+要高 所以*出栈,输出
同理 栈中的下一个元素+的优先级比读到的+的优先级高,所以也要进行出栈并且输出,然后将读到的+入栈7. 下一个读到(,它的优先级最高,直接入栈
8. 读到d 输出
9. 读到* 只有遇到)的时候左括号才会出栈,所以 *直接入栈
10. 读到e
11. 读到+ *出栈 然后把+进栈
12. 读到f
13. 遇到 )则直接将栈中元素出栈直到遇到( 为止
14. 读到* 进栈 读到g 输出
15. 读到字符数的结束,栈中还有两个操作符没有出栈,直接出栈并输出
6. 后缀表达式求值
后缀表达式也叫逆波兰表达式,求值过程中也是用到栈来辅助存储的
6 5 2 3 + 8 * + 3 + *
求值过程
1. 遍历表达式,遇到数字,进行入栈
2. 读到 + 则会出栈3和2 执行2+3 计算结果等于5 ,并将5进栈
3. 读到8 直接入栈
4. 读到 * 出栈两个元素8和5,计算5*8,将结果40入栈
5. 读到 + 出栈40和5 计算 5+40 将45进栈
6. 读到3 入栈
7. 读+ 出栈3和45 计算45+3 将48入栈
8. 读到* 出栈48 和 6 计算6*48 将288入栈
此时后缀表达式全部读完,计算结束,此时栈中唯一的元素就是最终的结果
计算器
实现计算器的 + - * / % 的功能 ,输入一串数学表达式(可能出现括号),计算这个表达式的结果
SequenceStack.h
#ifndef __SEQUENCESTACK_H__ #define __SEQUENCESTACK_H__ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAXSIZE 1024 typedef int SElemType; // 顺序栈的数据类型 typedef struct { // SElemType data[MAXSIZE]; SElemType *data; // data会指向一段地址连续的空间,来存储栈中的每个元素 int top; // 栈顶元素的下标 -1表示空栈,栈元素的个数 = top + 1 } SqStack; extern SqStack* init_stack(int max_size); // 初始化一个栈 extern SqStack* destroy_stack(SqStack *s); // 销毁一个栈 extern void clear_stack(SqStack *s); // 清空一个栈 extern bool StackEmpty(SqStack *s); // 判断栈是否为 extern void print_stack(SqStack *s); // 打印栈中所有元素 extern int stack_length(SqStack *s); // 返回栈的数据结点的个数(栈的长度) extern bool GetTop(SqStack *s, SElemType *d); // 获取栈顶元素的值,但是不出栈 extern bool Pop(SqStack *s, SElemType *d); // 出栈,把栈顶元素出栈,并返回 extern bool Push(SqStack *s, SElemType d); // 入栈(压栈),把数据存入栈中 #endif
SequenceStack.c
#include "SequenceStack.h" // 初始化一个栈 SqStack* init_stack(int max_size) { SqStack *s = malloc(sizeof(*s)); s->data = malloc(sizeof(SElemType) * max_size); s->top = -1; // 表示空栈 return s; } // 销毁一个栈 SqStack* destroy_stack(SqStack *s) { // 异常处理 if (s == NULL) { return NULL; } if (s->data) { // if (s->data != NULL) free(s->data); } free(s); s = NULL; return s; } // 清空一个栈 void clear_stack(SqStack *s) { // 异常处理 if (s == NULL || s->data == NULL) { return ; } s->top = -1; } // 判断栈是否为空 bool StackEmpty(SqStack *s) { // s不存在 || 栈为空 if (s == NULL || s->top == -1) { return true; } return false; } // 打印栈中所有元素 void print_stack(SqStack *s) { if (s == NULL || s->data == NULL || s->top == -1) { return ; } for (int i = 0; i <= s->top; i++) { printf("%d ", s->data[i]); } putchar('\n'); } // 返回栈的数据结点的个数(栈的长度) int stack_length(SqStack *s) { // 异常处理 if (s == NULL || s->data == NULL) { return 0; } return s->top + 1; } // 获取栈顶元素的值,但是不出栈 bool GetTop(SqStack *s, SElemType *d) { // 不能获取栈顶元素 if (s == NULL || s->data == NULL || s->top == -1) { return false; } // 能获取栈顶元素 *d = s->data[s->top]; return true; } // 出栈,把栈顶元素出栈,并返回 bool Pop(SqStack *s, SElemType *d) { // 不能出栈 if (s == NULL || s->data == NULL || s->top == -1) { return false; } // 出栈 获得元素 *d = s->data[s->top--]; return true; } // 入栈(压栈),把数据存入栈中 bool Push(SqStack *s, SElemType d) { // 不能入栈 if (s == NULL || s->data == NULL || s->top == MAXSIZE - 1) { return false; } // 入栈 s->data[++s->top] = d; // ->优先级高于++ return true; }
main.c
#include "SequenceStack.h" // 计算 int ji_suan(SqStack *sd, SqStack *so) { SElemType a, b; Pop(sd, &b); // 出栈 Pop(sd, &a); SElemType op; Pop(so, &op); switch (op) { case '+': return a + b; case '-': return a - b; case '*': return a * b; case '/': return a / b; case '%': return a % b; } } // 中缀——后缀——表达式求值 int zhong_to_expression(char *str) { if (str == NULL) { return 0; } // 1.初始化栈 SqStack *sd = init_stack(MAXSIZE); // 数字栈 SqStack *so = init_stack(MAXSIZE); // 运算符栈 // 2.遍历字符串 while (*str) { // 1.如果遇到操作数,我们就直接入数字栈 int sum = 0; if (*str >= '0' && *str <= '9') { while (*str >= '0' && *str <= '9') { sum = sum * 10 + (*str - '0'); // '0'的ASCII值是48 str++; } Push(sd, sum); // 入栈 } // 2.遇到左括号,将其入运算符栈;遇到右括号,则将栈中元素依次出栈直到遇到左括号为止。注意:左括号只是出栈并不输出 if (*str == '(') { Push(so, *str); } else if (*str == ')') { SElemType d; // 出栈 直到栈顶元素是左括号为止 while (!StackEmpty(so)) { // 获取栈顶元素 GetTop(so, &d); // 如果栈顶元素为左括号 直接出栈 停止出栈 if (d == '(') { Pop(so, &d); break; } else { // 如果栈顶元素不为左括号 直接出栈(两个操作数+1个运算符) SElemType ret = ji_suan(sd, so); Push(sd, ret); } } // 3.如果遇到任何的运算符,从栈中出栈并且输出直到遇到优先级更低的为止(或栈为空)。才将遇到的运算符入栈的操作 } else if (*str == '+' || *str == '-' || *str == '*' || *str == '/' || *str == '%') { // 不为空 while (!StackEmpty(so)) { SElemType top; // 保存栈顶元素 // 1.获取栈顶元素 GetTop(so, &top); // 2.比较 *str top // 如果栈顶元素为左括号 或者 代入栈的运算符优先级 高于 栈顶元素的优先级 入栈 直接跳出循环 if (top == '(' || (*str == '*' || *str == '/' || *str == '%') && (top == '+' || top == '-')) { Push(so, *str); // 入栈 break; } else { SElemType ret = ji_suan(sd, so); Push(sd, ret); } } // 为空 if (StackEmpty(so)) { Push(so, *str); } } str++; } // 4.如果我们读到了中缀表达式的末尾,则将栈中的所有元素依次出栈 while (!StackEmpty(so)) { SElemType ret = ji_suan(sd, so); Push(sd, ret); } // 最终的结果 进行出栈 SElemType res; Pop(sd, &res); sd = destroy_stack(sd); // 销毁栈 so = destroy_stack(so); // 销毁栈 return res; } int hou_to_expression(char *str) { // 1.初始化栈 SqStack *s = init_stack(MAXSIZE); while (*str) { if (*str == ' ') { str++; continue; } if (*str == '+' || *str == '-' || *str == '*' || *str == '/' || *str == '%') { SElemType a, b; Pop(s, &b); // 出栈 Pop(s, &a); SElemType res; switch (*str) { case '+': res = a + b; break; case '-': res = a - b; break; case '*': res = a * b; break; case '/': res = a / b; break; case '%': res = a % b; break; } Push(s, res); } else { int num = 0; while (*str && *str != ' ') { num = num * 10 + *str - '0'; str++; } Push(s, num); } str++; } SElemType res; Pop(s, &res); return res; } // 中缀转后缀 void zhong_to_hou(char *str) { if (str == NULL) { return ; } // 1.初始化一个栈 SqStack *s = init_stack(MAXSIZE); // 2.遍历字符串 while (*str != '\0') { // while (*str) // 1.如果遇到操作数,我们就直接输出 int sum = 0; if (*str >= '0' && *str <= '9') { while (*str >= '0' && *str <= '9') { sum = sum * 10 + (*str - '0'); // '0'的ASCII值是48 str++; } printf("%d ", sum); } // 2.遇到左括号,将其入栈;遇到右括号,则将栈中元素依次出栈直到遇到左括号为止。注意:左括号只是出栈并不输出 if (*str == '(') { Push(s, *str); } else if (*str == ')') { SElemType d; // 出栈 直到栈顶元素是左括号为止 while (!StackEmpty(s)) { // 出栈 Pop(s, &d); // 如果栈顶元素为左括号 停止出栈 if (d == '(') { break; } // 如果栈顶元素不为左括号 打印 printf("%c ", d); } // 3.如果遇到任何的运算符,从栈中出栈并且输出直到遇到优先级更低的为止(或栈为空)。才将遇到的运算符入栈的操作 } else if (*str == '+' || *str == '-' || *str == '*' || *str == '/' || *str == '%') { // 不为空 while (!StackEmpty(s)) { SElemType top; // 保存栈顶元素 // 1.获取栈顶元素 GetTop(s, &top); // 2.比较 *str top // 如果栈顶元素为左括号 或者 待入栈的运算符优先级 高于 栈顶元素的优先级 入栈 直接跳出循环 if (top == '(' || (*str == '*' || *str == '/' || *str == '%') && (top == '+' || top == '-')) { Push(s, *str); // 入栈 break; } else { Pop(s, &top); // 出栈 printf("%c ", top); } } // 为空 if (StackEmpty(s)) { Push(s, *str); } } str++; } // 4.如果我们读到了中缀表达式的末尾,则将栈中的所有元素依次出栈 while (!StackEmpty(s)) { SElemType top; // 保存栈顶元素 Pop(s, &top); printf("%c ", top); } destroy_stack(s); printf("\n"); } int main(int argc, char *argv[]) { char str[128] = {0}; gets(str); // zhong_to_hou(str); // printf("%s = %d\n", str, zhong_to_expression(str)); printf("%s = %d\n", str, hou_to_expression(str)); return 0; }
中缀转前缀
代码实现:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_SIZE 100 // 运算符优先级函数 int precedence(char op) { if (op == '+' || op == '-') { return 1; } else if (op == '*' || op == '/') { return 2; } else { return 0; } } // 中缀转前缀实现 void infix_to_prefix(char* infix, char* prefix) { char stack[MAX_SIZE]; // 定义一个栈,用来存放 运算符 int top = -1; // 定义 栈顶指针 int i, j, temp, k = 0; // 遍历到最后一个字符,k 最后指向 '\0' 的位置 while(infix[k] != '\0'){ k++; } // 从右至左遍历中缀字符数组 for (i = k - 1, j = 0; i >= 0; i--) { // 判断,如果是字符,将其存入前缀字符数组中 if (infix[i] >= 'a' && infix[i] <= 'z') { prefix[j++] = infix[i]; } else if (infix[i] == ')') { // 如果是 右括号,直接放入栈中 stack[++top] = infix[i]; } else if (infix[i] == '(') { // 如果是 左括号 // 将栈中 右括号之前的 运算符依次弹出,并进入前缀数组中 while (top != -1 && stack[top] != ')') { prefix[j++] = stack[top--]; } // 再减一次是为了:弹出 右括号 top--; } else { // 栈为不空,并且当栈顶运算符优先级 大于 扫描到中缀运算符优先级,将栈中元素依次弹出,放入前缀数组中 while (top != -1 && precedence(stack[top]) > precedence(infix[i])) { prefix[j++] = stack[top--]; } // 再将扫描到的中缀运算符,放入栈中 stack[++top] = infix[i]; } } // 当扫描完后,将栈中元素依次弹出,并放入前缀表达式数组中 while (top != -1) { prefix[j++] = stack[top--]; } // 进行逆置 for (i = 0, k = j - 1; i < k; i++, k--){ temp = prefix[i]; prefix[i] = prefix[k]; prefix[k] = temp; } // 设置字符数组结束符 prefix[j] = '\0'; } int main() { char infix[MAX_SIZE]; char prefix[MAX_SIZE]; printf("输入中缀表达式: "); scanf("%s", infix); infix_to_prefix(infix, prefix); printf("前缀表达式是: %s\n", prefix); return 0; }
7. 补充
1. 栈的分类
满栈:栈顶指针指向有内容的地址。因此,进栈是先移动指针再存;出栈是先出数据再移动指针
空栈:栈顶指针指向没有内容的地址。因此,进栈是先存再移动指针;出栈是先移动指针再出数据
减栈:栈顶指针向地址减小的方向移动。因此,进栈指针向下移动;出栈是指针向上移动
增栈:栈顶指针向地址增加的方向移动。因此,进栈指针向上移动;出栈是指针向下移动
四种栈情况:
1. 满减栈:栈顶指针一开始指向有内容的地址,进栈先向下移动指针后再将内容存进去,出栈先移动数据再向上移动指针
2. 满增栈:栈顶指针一开始指向有内容的地址,进栈先向上移动指针后再将内容存进去,出栈先移动数据再向下移动指针
3. 空减栈:栈顶指针一开始指向没有内容的地址,进栈先将内容存进去后再向下移动指针,出栈先向上移动指针再移动数据
4.空增栈:栈顶指针一开始指向没有内容的地址,进栈先将内容存进去后再向上移动指针,出栈先向下移动指针再移动数据
注:arm,gcc中使用的是满减栈
2. 共享栈(顺序)
现在需要用到两个栈,可能会出现:第一个栈已经满了,再进栈就溢出,而另一个栈还有很多的空间
解决上面的情况的办法将两个栈合起来,用一个数组来实现两个栈,共享栈
共享栈的4要素:
1.栈空: 栈1空:top1 == -1 栈2空:top2 == MAXSIZE
2.栈满: top1 = top2 - 1
3.元素x进栈: 进栈1 top1++; data[top] = x; 进栈2 top2--; data[top] = x;
4.出栈x: 出栈1 x = data[top1]; top1--; 出栈2 x = data[top2]; top2++;
typedef struct
{
Elemtype data[MAXSIZE]; //存放共享栈的元素
int top1, top2; //两个栈的栈顶的指针
}ShareStack;
如果要去操作共享栈,可能会不知道是操作哪个栈,在进行基本运算时增加一个形参 i, i 表示对哪个栈进行操作
如果i = 1表示对栈1进行操作,i = 2表示对栈2进行操作
代码实现:
ShareStack.h
#ifndef __SHARESTACK_H_ #define __SHARESTACK_H_ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAXSIZE 10 //定义共享栈中元素的最大个数 #define ElemType int //共享栈类型定义 typedef struct { ElemType data[MAXSIZE]; //静态数组存放栈中元素 int top1; //1号栈栈顶指针 int top2; //2号栈栈顶指针 }ShareStack; ShareStack* init_stack(); //初始化共享栈 bool Stack1Empty(ShareStack *ss); //1号栈判空 bool Stack2Empty(ShareStack *ss); //2号栈判空 bool Push1(ShareStack *ss, ElemType x); //1号栈入栈 bool Push2(ShareStack *ss, ElemType x); //2号栈入栈 bool Pop1(ShareStack *ss, ElemType *x); //1号栈出栈 bool Pop2(ShareStack *ss, ElemType *x); //2号栈出栈 bool GetTop1(ShareStack *ss, ElemType *x); //1号栈读取栈顶元素 bool GetTop2(ShareStack *ss, ElemType *x); //2号栈读取栈顶元素 ShareStack* destroy_stack(ShareStack *ss); //销毁共享栈 void print_stack1(ShareStack *ss); //打印栈1中所有元素 void print_stack2(ShareStack *ss); //打印栈2中所有元素 int stack_length(ShareStack *ss); //返回共享栈的数据结点的个数(栈的长度) #endif
ShareStack.c
#include "ShareStack.h" //初始化共享栈 ShareStack* init_stack() { ShareStack *ss = malloc(sizeof(*ss)); ss->top1 = -1; ss->top2 = MAXSIZE; return ss; } //1号栈判空 bool Stack1Empty(ShareStack *ss) { if (ss == NULL || ss->top1 == -1) return true; return false; } //2号栈判空 bool Stack2Empty(ShareStack *ss) { if (ss == NULL || ss->top2 == MAXSIZE) return true; return false; } //1号栈入栈操作 bool Push1(ShareStack *ss, ElemType x) { if (ss == NULL || ss->top1 + 1 == ss->top2) return false; ss->data[++ss->top1] = x; return true; } //2号栈入栈操作 bool Push2(ShareStack *ss, ElemType x) { if (ss == NULL || ss->top1 + 1 == ss->top2) return false; ss->data[--ss->top2] = x; return true; } //1号栈出栈操作:栈顶元素出栈 bool Pop1(ShareStack *ss, ElemType *x) { if (ss == NULL || ss->top1 == -1) return false; *x = ss->data[ss->top1--]; return true; } //2号栈出栈操作:栈顶元素出栈 bool Pop2(ShareStack *ss, ElemType *x) { if (ss == NULL || ss->top2 == MAXSIZE) return false; *x = ss->data[ss->top2++]; return true; } //1号栈读取栈顶元素 bool GetTop1(ShareStack *ss, ElemType *x) { if (ss == NULL || ss->top1 == -1) return false; *x = ss->data[ss->top1]; return true; } //2号栈读取栈顶元素 bool GetTop2(ShareStack *ss, ElemType *x) { if (ss == NULL || ss->top2 == MAXSIZE) return false; *x = ss->data[ss->top2]; return true; } //销毁共享栈 ShareStack* destroy_stack(ShareStack *ss) { if (ss == NULL) return ss; free(ss); ss = NULL; return ss; } //打印栈1中所有元素 void print_stack1(ShareStack *ss) { if (ss == NULL || ss->top1 == -1) return ; int j = ss->top1; for (int i = 0; i <= j; i++) printf("%d ", ss->data[i]); putchar('\n'); } //打印栈2中所有元素 void print_stack2(ShareStack *ss) { if (ss == NULL || ss->top2 == MAXSIZE) return ; int j = ss->top2; for (int i = MAXSIZE - 1; i >= j; i--) printf("%d ", ss->data[i]); putchar('\n'); } /* 返回共享栈的数据结点的个数(栈的长度) */ int stack_length(ShareStack *ss) { if (ss == NULL) //异常处理 return 0; return ss->top1 + 1 + MAXSIZE - ss->top2; }
main.c
#include "ShareStack.h" int main(int argc, char *argv[]) { ShareStack *ss = init_stack(); //初始化共享栈 //1号栈 if (Stack1Empty(ss)) //1号栈判空 printf("当前1号栈空!\n"); else printf("当前1号栈非空!\n"); int i = 5, val = 1; ElemType d; while (i--) { if (Push1(ss, val)) //1号栈入栈 printf("1号栈新元素%d入栈成功!\n", val++); else printf("共享栈已满,1号栈新元素入栈失败!\n"); if (GetTop1(ss, &d)) //1号栈读取栈顶元素 printf("1号栈读取栈顶元素成功,当前栈顶元素值为:%d\n", d); else printf("1号栈已空,读取栈顶元素失败!\n"); } if (Pop1(ss, &d)) //1号栈出栈 printf("1号栈栈顶元素出栈成功,出栈元素值为:%d\n", d); else printf("1号栈已空,栈顶元素出栈失败!\n"); print_stack1(ss); //打印栈1中所有元素 //2号栈 if (Stack2Empty(ss)) //2号栈判空 printf("当前2号栈空!\n"); else printf("当前2号栈非空!\n"); i = 5, val = 1; while (i--) { if (Push2(ss, val)) //2号栈入栈 printf("2号栈新元素%d入栈成功!\n", val++); else printf("共享栈已满,2号栈新元素入栈失败!\n"); if (GetTop2(ss, &d)) //2号栈读取栈顶元素 printf("2号栈读取栈顶元素成功,当前栈顶元素值为:%d\n", d); else printf("2号栈已空,读取栈顶元素失败!\n"); } if (Pop2(ss, &d)) //2号栈出栈 printf("2号栈栈顶元素出栈成功,出栈元素值为:%d\n", d); else printf("2号栈已空,栈顶元素出栈失败!\n"); print_stack2(ss); //打印栈2中所有元素 printf("共享栈长度:%d\n", stack_length(ss)); //返回共享栈的数据结点的个数(栈的长度) ss = destroy_stack(ss); //销毁共享栈 return 0; }
3. 函数的参数的入栈顺序(从右至左)
arm,gcc 满减栈
谁先入栈 谁的地址更大
针对arm gcc 入栈顺序就是从右往左
谁先入栈 先计算谁x = 1; printf("%d %d\n", x, x++); x = 1; printf("%d %d\n", x++, x); x = 1; printf("%d %d %d\n", x, x++, x); x = 1; printf("%d %d %d %d\n", x, ++x, x++, x);
2 1 1 2 2 1 2 3 3 1 3
1. 在处理printf()时,压栈顺序为从右往左,也就是说从右往左的计算(“计算”不等于“输 出”)。
2. 在计算时,遇到x++会记录此时的x的值作为最后的输出结果。
3. 遇到x和++x的时候则不会将此时的计算结果作为最终的输出,只会修改x的值,在最终输 出的时候都输出x的值(所以++x和x的结果总是一样的)。
原因:
1. 对于a++的结果,是有寻址函数栈空间来记录中间结果的,在最后给printf压栈的时候,再从栈中把中间结果取出来
2. 对于++a的结果,则直接压寄存器变量,寄存器经过了所有的自增操作
int i = 0; printf("%d %d %d %d %d %d %d", i, --i, i--, i, i++, ++i, i); //0 0 2 0 1 0 0
4. 局部变量的入栈顺序
arm,gcc 满减栈
1. 没有栈溢出的保护机制下编译
先定义 先入栈
void abc()
{
int a;
int b;
}
a->b &a > &b
2. 有栈溢出的保护机制下编译 (先申请,后入栈)
先按照类型区分,再按照定义变量的顺序去划分------> 先定义先申请
例外:char先申请 int后申请空间(编译器溢出保护的规定)
int a;
int b;
a->b &b > &a
char c;
int d;
c->d &d > &c
int e;
char f;
f->e &e > &f
形参:void abc(int a, int b, int c); // 从左往右先申请,后入栈局部变量和形参同时存在
先申请 后入栈
申请:形参 > 局部变量
入栈:&局部变量 > &形参