【数据结构】栈结构与应用

简介: 【数据结构】栈结构与应用

一、栈的概述

1、栈的介绍

  1. 栈的英文为stack
  2. 栈是一个先入后出(FILO-First In Last Out)的有序列表。
  3. 栈是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。
  4. 允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)
  5. 根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除

2、栈的应用场景

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

3、栈的代码实现

1)数组实现

package work.rexhao.stack;

public class arrayStackdemo {
   

    public static void main(String[] args) {
   
        arrayStact as = new arrayStact(10);
        as.push(1);
        as.push(2);
        as.list();
        System.out.println(as.pop());
        System.out.println(as.pop());
        as.list();
    }

}

class arrayStact {
   
    int maxSize;
    int[] data;
    int top;

    /**
     * 栈的构造器
     * 
     * @param maxSize 栈的大小
     */
    arrayStact(int maxSize) {
   
        this.maxSize = maxSize;
        data = new int[maxSize];
        top = -1;
    }

    /**
     * 判空
     */
    public boolean isEmpty() {
   
        return top == -1;
    }

    /**
     * 判满
     */
    public boolean isFull() {
   
        return top == maxSize - 1;
    }

    /**
     * 弹出
     */
    public int pop() {
   
        if (isEmpty()) {
   
            System.out.println("栈空");
            return -1;
        }
        return data[top--];
    }

    /**
     * 压栈
     */
    public void push(int num) {
   
        if (isFull()) {
   
            System.out.println("栈满");
        } else {
   
            data[++top] = num;
        }
    }

    /**
     * 遍历
     */
    public void list() {
   
        if (isEmpty()) {
   
            System.out.println("栈空");
            return;
        }
        for (int i = 0; i <= top; i++) {
   
            System.out.printf("data[%d] = %d\n", i, data[i]);
        }
    }
}

2)链表实现

package work.rexhao.stack;

public class linkedListStackDemo {
   
    public static void main(String[] args) {
   
        linkedListStack lls = new linkedListStack();
        try {
   
            lls.pop();
        } catch (Exception e) {
   
            System.out.println(e.getMessage());
        }
        lls.push(1);
        lls.push(2);
        lls.pop();
        lls.push(3);
        lls.push(4);
        lls.show();
    }
}

class linkedListStack {
   
    private stackNode head = new stackNode(-1);

    /**
     * 压栈
     */
    public void push(int num) {
   
        if (head.next == null) {
   
            head.next = new stackNode(num);
            return;
        }
        stackNode temp = head.next;
        while (true) {
   
            if (temp.next == null) {
   
                temp.next = new stackNode(num);
                return;
            }
            temp = temp.next;
        }

    }

    /**
     * 出栈
     */
    public int pop() {
   
        if (isEmpty()) {
   
            throw new RuntimeException("栈空");
        }
        stackNode temp = head;
        while (true) {
   
            if (temp.next.next == null) {
   
                break;
            }
            temp = temp.next;
        }
        int ans = temp.next.getData();
        temp.next = null;
        return ans;
    }

    /**
     * 判空
     */
    public boolean isEmpty() {
   
        return head.next == null;
    }

    /**
     * 遍历
     */
    public void show() {
   
        if (isEmpty()) {
   
            return;
        }
        stackNode temp = head.next;
        while (true) {
   
            if (temp == null) {
   
                return;
            }
            System.out.println(temp.toString());
            temp = temp.next;
        }
    }
}

class stackNode {
   
    private int data;
    public stackNode next;

    stackNode(int data) {
   
        this.data = data;
    }

    public int getData() {
   
        return data;
    }

    public void setData(int data) {
   
        this.data = data;
    }

    @Override
    public String toString() {
   
        return data + "";
    }
}

二、栈实现综合计算器

1、思路

  1. 通过一个index 值(索引),来遍历我们的表达式
  2. 扫描到一个数字,直接入数栈
  3. 扫描到一个符号
    1. 符号栈为空,就直接入栈
    2. 有操作符,就进行比较
      1. 如果当前的操作符的优先级小于或者等于栈顶的操作符,就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到结果入数栈,然后将当前的操作符入符号栈
      2. 如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈
  4. 当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号
  5. 最后在数栈只有一个数字,就是表达式的结果

2、代码实现

package work.rexhao.stack;

import java.util.Scanner;

/**
 * 个位数四则计算器
 *
 */
public class calculatorDemo {
   
    public static void main(String[] args) {
   
        System.out.println("请输入表达式");
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        sc.close();
        System.out.println("表达式值为:" + calculator.calculate(s));
    }

}

class calculator {
   

    /**
     * 计算
     * 
     * @param s 表达式
     * @return 结果
     */
    public static int calculate(String s) {
   
        /*
         * 初始化栈
         */
        numStact ns = new numStact(20); // 数栈
        signStact ss = new signStact(20); // 运算符栈

        /*
         * 扫描字符串
         */
        int slen = s.length();
        // int index = 0; // 扫描索引
        for (int i = 0; i < slen; i++) {
   
            char c = s.charAt(i);
            if (c == '*' || c == '/') {
   
                ss.push(c);
            } else if (c == '+' || c == '-') {
   
                while (true) {
   
                    // 栈空:入栈
                    if (ss.isEmpty()) {
   
                        break;
                    }
                    // 栈非空:比较
                    if (ss.top() == '+' || ss.top() == '-') {
   
                        // 同优先级
                        break;
                    } else {
   
                        // 大于优先级
                        // 1.取出两个数和运算符
                        int ans = count(ns.pop(), ns.pop(), ss.pop());
                        // 2.结果入栈
                        ns.push(ans);
                    }
                }
                ss.push(c);
            } else {
   
                ns.push(c - '0');
            }
        }
        while(ns.top != 0) {
   
            ns.push(count(ns.pop(), ns.pop(), ss.pop()));
        }
        return ns.pop();
    }

    /**
     * 辅助计算
     */
    private static int count(int a, int b, char c) {
   
        if (c == '+') {
   
            return a + b;
        } else if (c == '-') {
   
            return b - a;
        } else if (c == '*') {
   
            return a * b;
        } else if (c == '/') {
   
            return a / b;
        }
        throw new RuntimeException("NaN");
    }
}

/**
 * 数栈
 * 
 */
class numStact {
   
    int maxSize;
    int[] data;
    int top;

    /**
     * 栈的构造器
     * 
     * @param maxSize 栈的大小
     */
    numStact(int maxSize) {
   
        this.maxSize = maxSize;
        data = new int[maxSize];
        top = -1;
    }

    /**
     * 判空
     */
    public boolean isEmpty() {
   
        return top == -1;
    }

    /**
     * 判满
     */
    public boolean isFull() {
   
        return top == maxSize - 1;
    }

    /**
     * 弹出
     */
    public int pop() {
   
        if (isEmpty()) {
   
            System.out.println("栈空");
            return -1;
        }
        return data[top--];
    }

    /**
     * 压栈
     */
    public void push(int num) {
   
        if (isFull()) {
   
            System.out.println("栈满");
        } else {
   
            data[++top] = num;
        }
    }

    /**
     * 遍历
     */
    public void list() {
   
        if (isEmpty()) {
   
            System.out.println("栈空");
            return;
        }
        for (int i = 0; i <= top; i++) {
   
            System.out.printf("data[%d] = %d\n", i, data[i]);
        }
    }
}

/**
 * 符号栈
 *
 */
class signStact {
   
    int maxSize;
    char[] data;
    int top;

    /**
     * 栈的构造器
     * 
     * @param maxSize 栈的大小
     */
    signStact(int maxSize) {
   
        this.maxSize = maxSize;
        data = new char[maxSize];
        top = -1;
    }

    /**
     * 判空
     */
    public boolean isEmpty() {
   
        return top == -1;
    }

    /**
     * 判满
     */
    public boolean isFull() {
   
        return top == maxSize - 1;
    }

    /**
     * 弹出
     */
    public char pop() {
   
        if (isEmpty()) {
   
            throw new RuntimeException("栈空");
        }
        return data[top--];
    }

    /**
     * 压栈
     */
    public void push(char c) {
   
        if (isFull()) {
   
            System.out.println("栈满");
        } else {
   
            data[++top] = c;
        }
    }

    /**
     * 遍历
     */
    public void list() {
   
        if (isEmpty()) {
   
            System.out.println("栈空");
            return;
        }
        for (int i = 0; i <= top; i++) {
   
            System.out.printf("data[%d] = %s\n", i, data[i]);
        }
    }

    /**
     * 栈顶元素
     */
    public char top() {
   
        return data[top];
    }
}

三、逆波兰计算器

package work.rexhao.stack;

public class polandNotationDemo {
   

    public static void main(String[] args) {
   
        System.out.println(polandNotation.calculate("34+5*6-"));
    }

}

class polandNotation {
   
    public static int calculate(String s) {
   
        numStact ns = new numStact(20);
        // 不需要符号栈
        int slen = s.length();
        for (int i = 0; i < slen; i++) {
   
            char c = s.charAt(i);
            if (c == '*' || c == '/' || c == '+' || c == '-') {
   
                ns.push(count(ns.pop(), ns.pop(), c));
            } else {
   
                ns.push(c - '0');
            }
        }
        return ns.pop();
    }

    /**
     * 辅助计算
     */
    private static int count(int a, int b, char c) {
   
        if (c == '+') {
   
            return a + b;
        } else if (c == '-') {
   
            return b - a;
        } else if (c == '*') {
   
            return a * b;
        } else if (c == '/') {
   
            return a / b;
        }
        throw new RuntimeException("NaN");
    }
}

四、中缀转后缀

1、思路

后缀表达式适合计算式进行运算,但是却不太容易写出来,尤其是表达式很长的情况下

因此在开发中,我们常常需要将中缀表达式转成后缀表达式

  1. 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
  2. 从左至右扫描中缀表达式
  3. 遇到操作数时,将其压s2
  4. 遇到运算符时,比较其与 s1栈顶运算符的优先级
    1. 如果s1为空,或栈顶运算符为左括号(,压入s1
    2. 若该运算符优先级比栈顶运算符的高,压入s1
    3. 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到与与s1中新的栈顶运算符相比较
  5. 遇到括号
    1. 如果是左括号(,则直接压入s1
    2. 如果是右括号),则依次弹出s1的运算符压s2,直到遇到左括号为止,将这一对括号丢弃
  6. 重复步骤直到表达式的最右边
  7. 将s1中剩余的运算符依次弹出并压入s2
  8. 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

2、代码实现

/**
 * 中缀转后缀
 * 
 * @param s 中缀表达式
 * @return 后缀表达式
 */
public static String toPoland(String s) {
   
    sStact s1 = new sStact(20);// 运算符栈
    sStact s2 = new sStact(20);// 中间结果栈
    int slen = s.length();
    for (int i = 0; i < slen; i++) {
   
        char c = s.charAt(i);
        if (s1.isEmpty() || c == '(') {
   
            s1.push(c);
        } else if (c == ')') {
   
            while (true) {
   
                char temp = s1.pop();
                if (temp == '(') {
   
                    break;
                }
                s2.push(temp);
            }
        } else if (c == '+' || c == '-') {
   
            while (true) {
   
                if (s1.isEmpty() || s1.top() == '(') {
   
                    s1.push(c);
                    break;
                }
                s2.push(s1.pop());
            }
        } else if (c == '*' || c == '/') {
   
            while (true) {
   
                if (s1.isEmpty() || s1.top() == '+' || s1.top() == '-' || s1.top() == '(') {
   
                    s1.push(c);
                    break;
                }
                s2.push(s1.pop());
            }
        } else {
   
            // 数字
            s2.push(c);
        }
    }
    while (!s1.isEmpty()) {
   
        s2.push(s1.pop());
    }
    StringBuilder sb = new StringBuilder();
    while (!s2.isEmpty()) {
   
        sb.append(s2.pop());
    }
    return sb.reverse().toString();
}
目录
相关文章
|
10天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
81 9
|
4天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
9天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
50 16
|
1天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
8 1
|
7天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
9天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
32 4
|
13天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
18天前
|
机器学习/深度学习 存储 人工智能
数据结构在实际开发中的广泛应用
【10月更文挑战第20天】数据结构是软件开发的基础,它们贯穿于各种应用场景中,为解决实际问题提供了有力的支持。不同的数据结构具有不同的特点和优势,开发者需要根据具体需求选择合适的数据结构,以实现高效、可靠的程序设计。
42 7
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
27 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
初步认识栈和队列
初步认识栈和队列
57 10