栈的介绍

栈是限制插入和删除只能在一个位置上进行的线性表。其中允许插入和删除的一端位于表的末端,叫做栈顶(top);不允许插入和删除的另一端为栈底(bottom)。对栈的基本操作有push(压栈)pop(出栈、弹栈),前者相当于表的插入操作(向栈顶插入一个元素),后者则是删除操作(删除一个栈顶元素)。栈式一种后进先出,先进后出(LIFO)的数据结构,最先被删除的是最近压栈的元素。

遵循**先进后出,后进先出**原则

  • 取出数据: 出栈(弹栈)
  • 插入数据: 压栈

栈实现

由于栈是一个表,因此任何实现表的方法都可以用来实现栈。主要有俩种方式,链表实现数组实现

链表实现栈

可以使用单链表来实现栈。通过在表顶端插入一个来实现push,通过删除表顶端元素来实现pop。使用链表实现的的栈又叫动态栈。动态栈有链表的部分特性,即元素与元素之间在物理存储上可以不连续,但是功能有些受限,动态栈只能在栈顶处进行插入和删除操作,不能在栈尾或者栈中间进行插入和删除操作

数组实现栈

栈也可以用数组来实现。使用数组方式实现的栈叫做静态栈

栈的应用场景
  • 子程序的调用: 在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中
  • 处理递归调用: 和子程序的调用类似,只是除了存储下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
  • 表达式的转换[中缀表达式转后缀表达式]与求值(实际解决)
  • 二叉树的遍历
  • 图形的深度优先(depth->first) 搜索法

模拟栈实现(数组)

  1. package com.chentianyu.stack;
  2. public class ArrayStack {
  3. //栈的大小
  4. private int maxStack;
  5. //数组来模拟栈
  6. private int[] stack;
  7. //表示栈顶所在的位置,默认情况下,没有数据时,使用-1
  8. private int top = -1;
  9. //给栈设置初始化大小
  10. public ArrayStack(int maxStack){
  11. this.maxStack = maxStack;
  12. //初始化栈
  13. stack = new int[maxStack];
  14. }
  15. /**
  16. * 1.压栈
  17. * 2.弹栈
  18. * 3.判断是否是空栈
  19. * 4.当前栈中是否是满栈的状态
  20. */
  21. //判断是否已经满栈
  22. public boolean isFull(){
  23. return this.top == maxStack - 1;
  24. }
  25. //判断当前栈是否是一个空栈(栈里有没有元素)
  26. public boolean isEmpty(){
  27. return this.top == -1;
  28. }
  29. //压栈
  30. public void push(int val){
  31. //是否已经栈满
  32. if(isFull()){
  33. throw new RuntimeException("栈已满");
  34. }
  35. top++;
  36. stack[top] = val;
  37. }
  38. //弹栈
  39. public int pop(){
  40. //如果栈是空的
  41. if(isEmpty()){
  42. throw new RuntimeException("栈是空的,没有数据");
  43. }
  44. int value = stack[top];
  45. top--;
  46. return value;
  47. }
  48. //该方法查看栈中所有的元素
  49. public void list(){
  50. if(isEmpty()){
  51. throw new RuntimeException("栈是空的,没有数据");
  52. }
  53. for (int i=0;i<stack.length;i++){
  54. System.out.printf("stack[%d]=%d",i,stack[i]);
  55. }
  56. }
  57. /**
  58. * 栈中元素存在的个数
  59. * @return
  60. */
  61. public int length(){
  62. return this.top + 1;
  63. }
  64. }

栈(回文数据)
  • 回文: 指一个单词(或数据)从前往后,从后往前都是一样的
  1. package com.chentianyu.stack;
  2. public class ArrayStackTest {
  3. public static void main(String[] args) {
  4. /**
  5. * 回文数据
  6. * 回文: 指一个单词(或数据)从前往后,从后往前都是一样的
  7. * 需求: 通过上面以数据模拟栈来判断一个字符是否是一个回文数据
  8. */
  9. System.out.println(detecation("hello"));
  10. }
  11. public static boolean detecation(String val){
  12. /**
  13. * 初始化栈对象
  14. */
  15. ArrayStack arrayStack = new ArrayStack(10);
  16. /**
  17. * 获取字符串的长度
  18. */
  19. int length = val.length();
  20. //把字符串数据逐次获取字符压栈至数组中
  21. for (int i=0;i<length;i++){
  22. arrayStack.push(val.charAt(i));
  23. }
  24. /**
  25. * 获取
  26. */
  27. String newVal = "";
  28. for (int i=0;i<arrayStack.length();i++){
  29. //是否是一个空栈
  30. if(!arrayStack.isEmpty()){
  31. //强制类型转换
  32. char pop = (char) arrayStack.pop();
  33. newVal = newVal + pop;
  34. }
  35. }
  36. if(val.equals(newVal)){
  37. return true;
  38. }
  39. return false;java
  40. }
  41. }

每次arrayStack.length()循环的数字不是一个固定的值,在递减可以考虑把长度拿出循环,在外面付给一个变量

package com.chentianyu.stack;

public class ArrayStackTest {
    public static void main(String[] args) {
        /**
         * 回文数据
         * 回文: 指一个单词(或数据)从前往后,从后往前都是一样的
         * 需求: 通过上面以数据模拟栈来判断一个字符是否是一个回文数据
         */
        System.out.println(detecation("hello"));
    }
    //判断是否是回文数据
    public static boolean detecation(String val){
        /**
         * 初始化栈对象
         */
        ArrayStack arrayStack = new ArrayStack(10);
        /**
         * 获取字符串的长度
         */
        int length = val.length();
        //把字符串数据逐次获取字符压栈至数组中
        for (int i=0;i<length;i++){
            arrayStack.push(val.charAt(i));
        }
        /**
         * 获取
         */
        String newVal = "";
//        for (int i=0;i<arrayStack.length();i++){//每次arrayStack.length()循环的数字不是一个固定的值,在递减可以考虑把长度拿出循环,在外面付给一个变量
          int len = arrayStack.length();
          for (int i=0;i<len;i++){
            //是否是一个空栈
            if(!arrayStack.isEmpty()){
                //强制类型转换
                char pop = (char) arrayStack.pop();
                newVal = newVal + pop;
            }
        }
        if(val.equals(newVal)){
            return true;
        }
        return false;
    }
}

aba是回文数据,返回true

hello不是回文数据,返回false

栈(计算机需求分析)

使用栈完成一个表达式计算结果:**4+3+2+1*5**或者是一个**"4+3+2+1*5"**的数据

思路: 定义俩个栈: 数字栈符号栈。当获取到的是符号是数字,就会压栈到数字栈中,把符号压入符号栈

  • 1.循环遍历出来每一个元素
  • 2.如果是数字则压入数字栈,如果是一个符号则压入符号栈。
  • 3.如果符号(存在优先级问题)
    • 如果符号栈是空,则直接入栈
    • 如果符号栈中不为空,则对比栈中符号的优先级;如果优先级小于等于栈中的符号,则先计算数字栈中的数据,将得到的结果再次入栈,再把符号入符号栈(比如栈顶是*压栈了一个-,-小于*的优先级,所以需要它原本的数据先进行运算得到的结果再次入栈);如果优先级大于符号栈的符号,直接将符号入栈即可
//ArrayStack.java
/**
 * 判断是否是一个运算符 + - * /
 */
public boolean isOper(char v){
    return v == '+'||v == '-'||v == '*'|| v == '/';
}
/**
 * 判断运算符优先级,使用数字表示优先级大小;数字越大优先级越大
 */
public int priority(int oper){
    if(oper == '*' || oper == '/'){
        return 1;
    }else if(oper == '+' || oper == '-'){
        return 0;
    }else {
        return -1;
    }
}
/**
 * 查看栈顶数据
 */
public int peek(){
    return this.stack[top];
}
/**
 * 获取栈的容量
 */
public int stackLength(){
    return this.stack.length;
}
/**
* 计算俩个数进行运算后的结果
*/
public int calculate(int num1,int num2,int oper){
    //计算结果
    int result = 0;
    switch (oper){
        case '+': result = num1 + num2;break;
        case '-': result = num2 - num1;break;
        case '*': result = num1 * num2;break;
        case '/': result = num2 /num1;break;
        default:break;
    }
    return result;
}

验证结果

//判断字符串的
public static void yunSuan(String str){

    /**
     * 1.需要遍历当前字符串,获取每一个字符
     * 2.判断当前字符是一个运算符还是一个数字
     * 3.把数字存放在数字栈中,把运算符放在运算符栈中
     * 4.运算符栈如果是一个空战,则运算符直接入栈;如果运算符栈中存在其他的运算符,那我们就需要对比运算符的优先级: 新进来的运算优先级如果小于等于原栈中的运算符,那么就需要把原原运算符弹栈,数字栈中的数据进行弹栈运算,运算后的结果重新放入数字栈中
     * 如果新的运算符优先级大于原符号栈中运算符,那么新的符号直接入栈
     */
    ArrayStack numStack = new ArrayStack(10);//数字栈
    ArrayStack symbolStack = new ArrayStack(10);//符号栈

    /**
     * 获取字符串长度
     */
    //拿取数字栈中第一个数字
    int temp1 = 0;
    //拿取数字栈中第二个数字
    int temp2 = 0;
    //拿字符
    int symbolChar = 0;
    //结果
    int result = 0;
    int length = str.length();
    for (int i=0;i<length;i++){
        char c = str.charAt(i);
        //是否是一个运算符
        if(symbolStack.isOper(c)){
            String values = "";
            //如果不是一个空栈
            if(!symbolStack.isEmpty()){
                //比较运算符优先级
                if(symbolStack.priority(c)<=symbolStack.priority(symbolStack.peek())){
                    /**
                     * 1.去符号栈中获取栈顶符号
                     * 2.去数字栈中获取俩个数字
                     */
                    temp1 = numStack.pop();
                    temp2 = numStack.pop();
                    symbolChar = symbolStack.pop();
                    //result俩个数字之间的运算结果
                    result = numStack.calculate(temp1,temp2,symbolChar);
                    //把运算结果再次放入数字栈中
                    numStack.push(result);
                    //把当前符号压入符号栈中
                    symbolStack.push(c);
                }else {
                    //符号直接压进符号栈;如果是空符号栈,将运算符直接压栈
                    symbolStack.push(c);
                }
            }else {
                //比如 33+44
                values += c;
                if(i == length-1){
                    numStack.push(Integer.parseInt(values));
                }else {
                    //如果后面还不是一个符号
                    char data = str.substring(i+1,i+2).charAt(0);
                    if (symbolStack.isOper(data)){
                        numStack.push(Integer.parseInt(values));
                        //放入栈后清空values
                        values = "";
                    }
                }


        }
    }
        while (true){
            if (symbolStack.isEmpty()){
                break;
            }
            temp1 = numStack.pop();
            temp2 = numStack.pop();
            symbolChar = symbolStack.pop();
            result = numStack.calculate(temp1,temp2,symbolChar);
            numStack.push(result);
        }
        int res = numStack.pop();
        System.out.println("结果是:" + res);
    }
}