【数据结构】栈

一、栈的基本介绍

1.1 栈的基本性质

  • 栈的英文为(stack)

  • 栈是一个先入后出(FILO-First In Last Out)的有序列表

  • 栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。 允许插入和删除的一端, 为变化的一端, 称为栈顶(Top), 另一端为固定的一端, 称为栈底(Bottom)。

  • 根据栈的定义可知, 最先放入栈中元素在栈底, 最后放入的元素在栈顶, 而删除元素刚好相反, 最后放入的元素最先删除, 最先放入的元素最后删除

  • 图解方式说明出栈(pop)和入栈(push)的概念

  

1.3、栈的应用场景

  • 子程序的调用: 在跳往子程序前, 会先将下个指令的地址存到堆栈中, 直到子程序执行完后再将地址取出, 以回到原来的程序中。

  • 处理递归调用: 和子程序的调用类似, 只是除了储存下一个指令的地址外, 也将参数、 区域变量等数据存入栈中。

  • 表达式的转换:[中缀表达式转后缀表达式]与求值(实际解决)。

  • 二叉树的遍历

  • 图形的深度优先(depth 一 first)搜索法

二、数组模拟栈

2.1、代码思路

  • maxSize :栈的大小(数组的大小)
  • arr :用来模拟栈的数组
  • top :指向当前栈顶元素,初始值为 -1 ,表示栈空
  • 判断栈满:top == maxSize ,即已经到达数组最后一个位置
  • 判断栈空:top == -1
  • 入栈:arr[++top] = arr;
  • 出栈:return arr[top–] ;

  

2.2、代码实现

  • 代码
     1 public class ArrayStackDemo {
     2     public static void main(String[] args) {
     3         // 测试以下ArrayStack 是否正确
     4         // 先创建一个ArrayStack对象-> 表示栈
     5         ArrayStack stack = new ArrayStack(4);
     6         String key = "";
     7         boolean loop = true;// 控制是否退出菜单
     8         Scanner scanner = new Scanner(System.in);
     9         while (loop) {
    10             System.out.println("show:表示显示栈");
    11             System.out.println("push:表示添加数据到栈(入栈)");
    12             System.out.println("pop:表示从栈取出数据(出栈)");
    13             System.out.println("exit:退出程序");
    14             System.out.println("请输入你的选择:");
    15             switch (key = scanner.next()) {
    16                 case "show":
    17                     stack.list();
    18                     break;
    19                 case "push":
    20                     System.out.println("请输入一个数");
    21                     int value = scanner.nextInt();
    22                     stack.push(value);
    23                     break;
    24                 case "pop":
    25                     int v = stack.pop();
    26                     System.out.println("取出一个数:" + v);
    27                     break;
    28                 case "exit":
    29                     loop = false;
    30                     break;
    31                 default:
    32                     System.out.println("输入错误");
    33             }
    34             System.out.println();
    35         }
    36     }
    37 }
    38 
    39 // 定义一个ArrayStack 表示栈
    40 class ArrayStack {
    41     private int maxSize;// 栈的大小
    42     private int[] stack;// 数组,数组模拟栈,数据就放在该数组
    43     private int top = -1;// top表示栈顶,初始化为-1
    44 
    45     // 构造器
    46     public ArrayStack(int maxSize) {
    47         this.maxSize = maxSize;
    48         stack = new int[this.maxSize];
    49     }
    50 
    51     // 栈满
    52     public boolean isFull() {
    53         return top == maxSize - 1;
    54     }
    55 
    56     // 栈空
    57     public boolean isEmpty() {
    58         return top == -1;
    59     }
    60 
    61     // 入栈-push
    62     public void push(int value) {
    63         // 先判断栈是否满
    64         if (isFull()) {
    65             System.out.println("栈满");
    66             return;
    67         }
    68         ++top;
    69         stack[top] = value;
    70     }
    71 
    72     // 出栈-pop
    73     public int pop(){
    74         // 先判断是否为空
    75         if(isEmpty()){
    76             // 抛出异常
    77             throw new RuntimeException("栈空,没有数据~");
    78         }
    79         int value = stack[top];
    80         top--;
    81         return value;
    82     }
    83 
    84     // 显示栈的情况【遍历栈】,遍历时,需要重栈顶开始显示数据
    85     public void list(){
    86         if (isEmpty()) {
    87             System.out.println("栈空,没有数据~");
    88             return;
    89         }
    90         for(int i = top; i > -1; i--){
    91             System.out.printf("stack[%d] = %d
    ", i, stack[i]);
    92         }
    93     }
    94 }

三、栈实现综合计算器

  目标:实现一个简单的计算器,计算不带括号的一位数表达式(中缀表达式),如:"7*2*2-5+1-5+3-4"

3.1 使用栈完成表达式的计算

  1. 通过一个index(索引),来遍历表达式

  2. 如果是一个数字,就直接入栈(存放数字的栈)

  3. 如果是一个符号:

      3.1 如果符号栈为空,符号直接入栈

      3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或等于栈中的操作符,就需要从数栈中pop出两个数,从符号栈中pop出一个符号,进行运算,将得到的结果入数栈,然后将当前操作符入符号栈;如果当前的操作符的优先级大于栈中的操作符,就直接入符号

  4. 当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运算

  5. 最后在数栈只有一个数字,就是表达式的结果

  注意下面代码:单个数字,表达式中间不要有空格

  1 public class Calculator {
  2     public static void main(String[] args) {
  3         String expression = "7+2*6-2"; //目前方法字符串中不要有空格
  4         //创建两个栈,数栈和符号栈
  5         ArrayStack2 numStack = new ArrayStack2(10);
  6         ArrayStack2 operStack = new ArrayStack2(10);
  7         //辅助变量
  8         int index = 0;
  9         int num1 = 0;
 10         int num2 = 0;
 11         int oper = 0; //也可以用char
 12         int res = 0;
 13         char ch = ' '; //保存每次扫描得到的char
 14         //使用while循环扫描expression
 15         while(true) {
 16             //依次得到expression的每一个字符
 17             ch = expression.substring(index, index + 1).charAt(0);
 18             //判断ch
 19             if(operStack.isOper(ch)) { //如果是运算符
 20                 if(!operStack.isEmpty()) {
 21                     //不为空
 22                     if(operStack.priority(ch) <= operStack.priority(operStack.peek())) {
 23                         num1 = numStack.pop();
 24                         num2 = numStack.pop();
 25                         oper = operStack.pop();
 26                         res = numStack.cal(num1, num2, oper);
 27                         //把运算结果入数栈
 28                         numStack.push(res);
 29                         //把当前的符号入符号栈
 30                         operStack.push(ch);
 31                     } else {
 32                         operStack.push(ch);
 33                     }
 34                 } else {
 35                     //为空,直接进栈
 36                     operStack.push(ch);
 37                 }
 38             } else { //如果是数,则直接入数栈
 39                 numStack.push(ch - 48); //因此ch是字符型,查Ascii码看为啥要-48
 40             }
 41             //让index + 1,并判断是否到最后
 42             index++;
 43             if(index >= expression.length()) {
 44                 break;
 45             }
 46         }
 47         //当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运算
 48         while(true) {
 49             //如果符号栈为空,则计算到最后的结果,数栈中只有一个数值
 50             if(operStack.isEmpty()) {
 51                 break;
 52             }
 53             num1 = numStack.pop();
 54             num2 = numStack.pop();
 55             oper = operStack.pop();
 56             res = numStack.cal(num1, num2, oper);
 57             numStack.push(res);
 58         }
 59         int res2 =numStack.pop();
 60         System.out.printf("表达式:%s=%d",expression , res2);
 61     }
 62 }
 63 
 64 //先创建一个栈
 65 class ArrayStack2{
 66     private int maxSize; //栈的大小
 67     private int[] stack; //数组,数据放入该数组中
 68     private int top = -1; //栈顶,初始化为-1,表示没有数据
 69     
 70     public ArrayStack2(int maxSize) {
 71         this.maxSize = maxSize;
 72         stack = new int[this.maxSize]; //在构造器中初始化数组,不然无法存储数据
 73     }
 74     
 75     //判断栈满
 76     public boolean isFull() {
 77         return top == maxSize - 1;
 78     }
 79     
 80     //判断栈空
 81     public boolean isEmpty() {
 82         return top == -1;
 83     }
 84     
 85     //入栈 -push
 86     public void push(int value) {
 87         //先判断栈是否满
 88         if(isFull()) {
 89             System.out.println("栈满");
 90             return;
 91         }
 92         top++;
 93         stack[top] = value;
 94     }
 95     
 96     //出栈-pop,将栈顶的数据返回
 97     //根据数组类型,选择返回类型,不一定非是int
 98     public int pop() {
 99         //先判断是否为空
100         if(isEmpty()) {
101             //抛出异常(代表终止了,不需要return)
102             //因为这个函数有返回值,如果使用return处理不好处理
103             throw new RuntimeException("栈空,没有数据");
104         }
105         //首先取得栈顶的值
106         int temp = stack[top]; //此处的类型根据数组类型选择,同返回类型一至
107         top--;
108         return temp;
109     }
110     
111     //显示栈的情况【遍历栈】
112     public void list() {
113         if(isEmpty()) {
114             System.out.println("栈空,没有数据");
115             return;
116         }
117         //遍历时候要从栈顶开始
118         for(int i = top; i >= 0; i--) {
119             System.out.printf("stack[%d]=%d
", i , stack[i]);
120         }
121     }
122     
123     //返回运算符的优先级,优先级是程序员来确定,优先级使用数字表示,数字越大,则优先级越高
124     public int priority(int oper) {
125         if(oper == '*' || oper == '/') { //char和int 可以直接比较
126             return 1;
127         } else if(oper == '+' || oper == '-') {
128             return 0;
129         } else {
130             return -1; //假设目前的表达式只有+-*/
131         }
132     }
133     
134     //判断是不是一个运算符
135     public boolean isOper(char val) {
136         return val == '+' || val == '-' || val == '*' || val == '/';
137     }
138     
139     //计算方法
140     public int cal(int num1, int num2, int oper) {
141         int res = 0; //存放计算的结果
142         switch (oper) {
143         case '+':
144             res = num1 + num2;
145             break;
146         case '-':
147             res = num2 - num1; //把后弹出的数作为减数
148             break;
149         case '*':
150             res = num1 * num2;
151             break;
152         case '/':
153             res = num2 / num1;
154             break;
155         default:
156             break;
157         }
158         return res;
159     }
160     
161     //返回当前栈顶的值,不是真正出栈
162     public int peek() {
163         return stack[top];
164     }
165 }

原文链接:https://blog.csdn.net/oneby1314/article/details/107843972

原文地址:https://www.cnblogs.com/h--d/p/14569533.html