Java-栈

目录

 栈的相关问题

不可能的出栈顺序:

前、中、后缀表达式

栈的常用方法

栈的相关OJ题

逆波兰表达式

验证栈序列

有效的括号

最小栈

模拟实现栈


 

数据结构就是用来存储数据的一种方式。

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFOLast In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据在栈顶。

Java-栈_第1张图片

了解了栈后,有时可能会遇到这样一个面试问题:

Java-栈_第2张图片

 栈的相关问题

不可能的出栈顺序:

Java-栈_第3张图片

我们已经知道了栈是后进先出的,但我们不要忽视一点,入栈后是可以出栈的,不必要入完后才出,也就数说可以一边入栈一边出栈,所以本题答案选C。

前、中、后缀表达式

给你一个中缀表达式,把它转化为前缀表达式或后缀表达式,注:我们平时写的算式是中缀表达式。

笔试题:按运算顺序为每个运算添上一个括号,再将运算符移到加的括号后边,最后删掉括号即可。

中缀转后缀:

Java-栈_第4张图片

 同理,将运算符移到最近的括号前面即使前缀表达式。

中缀转前缀:

Java-栈_第5张图片

栈的常用方法

Java-栈_第6张图片

运用:

public class Test {
    public static void main(String[] args) {
        Stackstack = new Stack<>();
        stack.push(1);
        stack.push(2);
        stack.push(3);
        stack.push(4);
        System.out.println(stack);
        System.out.println(stack.peek());//查看栈顶元素且不删除
        System.out.println(stack.pop());//删除栈顶元素并返回栈顶元素的值
        System.out.println(stack.peek());

        stack.pop();
        stack.pop();
        stack.pop();
        System.out.println(stack.empty());//判断栈元素是否为空,是则返回true
        System.out.println(stack.isEmpty());//与上面那个方法一样
        System.out.println(stack);
    }
}

结果:

Java-栈_第7张图片

栈的相关OJ题

逆波兰表达式

根据逆波兰表达式,求表达式的值。

有效的算符包括 +-*/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

还记得我们先说的后缀表达式吗,后缀表达式其实就是逆波兰表达式。

注意 两个整数之间的除法只保留整数部分。

可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

示例 1:

输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

示例 2:

输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

示例 3:

输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

 解题思路:

不是专门写讲解题目的,因此方法不是很巧妙,重点是明白栈的基本用法。

这道题其实不是特别难,首先分析,这道题给的是一个字符串数组,而每一个字符串不是数字就是运算符。因此我们可以设计一个函数来判断是否是运算符,如果是就返回true,不是就返回false:

private boolean isOperation(String s){
        if(s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/")){
            return true;
        }
        return false;
    }

接着我们要明白,我们原来后缀表达式是把运算符放到了一个运算式的后面即把 1+1变为了1  1  + ,这就代表每两个数才会进行一次计算。因此我们可以设立一个下标 i 指向字符串数组for循环遍历,判断该字符串是否是数字,如果是数字,压入栈中,不是则从栈中弹出两个元素进行运算。这时我们就要注意一个问题,先出栈的是放在运算符的左边还是右边,我们可以想象一下,栈是后进先出,因此放右边的肯定是先出来的那个。然后将运算的结果再压入栈中。最后i到达边界了,那么栈中的数字就是运算结果。

代码实现:

class Solution {
    public int evalRPN(String[] tokens) {
        Stack stack = new Stack<>();
        for (int i = 0; i < tokens.length; i++) {
            String val = tokens[i];
            if(!isOperation(val)){
                stack.push(Integer.parseInt(val));
            }else{
                int num2 = stack.pop();//先弹出来的在右边
                int num1 = stack.pop();
                switch (val){
                    case "+":
                        stack.push(num1 + num2);
                        break;
                    case "-":
                        stack.push(num1 - num2);
                        break;
                    case "*":
                        stack.push(num1 * num2);
                        break;
                    case "/":
                        stack.push(num1 / num2);
                        break;
                }
            }
        }
        return stack.pop();
    }
    private boolean isOperation(String s){
        if(s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/")){
            return true;
        }
        return false;
    }
}

力扣OJ链接:逆波兰表达式。

验证栈序列

给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。

 这道题与我们最开始不可能的出栈顺序有点类似。即pushed是入栈顺序,poped是出栈顺序,如果两者不匹配,则返回false,匹配则返回true。

示例 1:

输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1

示例 2:

输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。

解题思路:

首先我们分析之前做不可能的出栈顺序是怎们想的,因为在元素入栈后就可以出栈也可以继续入栈新元素。因此我们可以设立一个下标 j 指向出栈数组。新建一个栈,当入栈的元素不等于出栈的元素时,入栈该元素,如果相等,则下标 j ++,将该元素出栈。最后如果栈为空就返回true,否则返回false。

代码实现:

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Stackstack = new Stack<>();
        int j = 0;
        for (int i = 0; i < pushed.length; i++) {
           stack.push(pushed[i]);
           while(j < popped.length && !stack.isEmpty() && stack.peek() == popped[j]){
               stack.pop();
               j++;
           }
        }
        return stack.isEmpty();
    }
}

力扣OJ链接:验证栈序列

有效的括号

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

    左括号必须用相同类型的右括号闭合。
    左括号必须以正确的顺序闭合。

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

示例 3:

输入:s = "(]"
输出:false

解题思路:

遍历数组,如果是左括号则在栈中存入一个右括号,如果不是左括号则出栈一个元素,与这个相匹配,如果不相等则返回false,如果最后栈为空则返回true。

代码实现:

class Solution {
    public boolean isValid(String s) {
        Stack stack = new Stack<>();
        for(char c : s.toCharArray()){
            if(c == '(')stack.push(')');
            else if(c == '{')stack.push('}');
            else if(c == '[')stack.push(']');
            else if(stack.isEmpty() || c !=stack.pop())return false;
        }
        return stack.isEmpty();
    }
}

力扣OJ题链接:有效的括号

最小栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

    push(x) —— 将元素 x 推入栈中。
    pop() —— 删除栈顶的元素。
    top() —— 获取栈顶元素。
    getMin() —— 检索栈中的最小元素。

这个题相比一个栈,就多了一个一个方法获取最小值。

解题思路:

我们可以设计两个栈,一个栈用于正常的入栈出栈,一个栈用来存储最小值。

一开始将第一个元素都存入两个栈中,而后,之后的元素普通栈对应入栈出栈,而最小栈则在只有元素小于或等于最小栈的栈顶元素时才入栈。出栈时,只有出栈元素与最小栈的栈顶元素相等时才出栈。

代码实现:

class MinStack {
    Stackstack ;
    StackminStake;
    public MinStack() {
        stack = new Stack<>();
        minStake = new Stack<>();
    }

    public void push(int val) {
        if(!minStake.isEmpty() && minStake.peek() >= val){
            minStake.push(val);
        }else if(minStake.isEmpty()){
            minStake.push(val);
        }
        stack.push(val);
    }

    public void pop() {
        int sq = stack.pop();
        if(!minStake.isEmpty() && sq == minStake.peek()){
            minStake.pop();
        }
    }

    public int top() {
        return stack.peek();
    }

    public int getMin() {
        return minStake.peek();
    }
}

力扣OJ链接:最小栈

模拟实现栈

前面的题和讲解我们也逐渐熟悉了栈,那么来模拟实现一下栈吧,简单模拟一下。

public class myStack {
    public E []elem;
    public static int usedSize = 0;

    public myStack(){
        this.elem =(E[]) new Object[5];
    }

    public void push(E e){
        if(isFull()){
            this.elem = Arrays.copyOf(elem,elem.length * 2);//2倍扩容
        }
        this.elem[usedSize] = e;
        usedSize++;
    }

    public boolean isEmpty(){
        return usedSize == 0;
    }

    public boolean isFull(){
        return usedSize == elem.length;
    }

    public E pop(){
        if(isEmpty()){
            return null;
        }
        if(usedSize > 0) {
            E e = this.elem[usedSize-1];
            usedSize--;
            return e;
        }
        return null;
    }

    public E peek(){
        return this.elem[usedSize-1];
    }

    public String toString() {//重写toString方法
        StringBuilder stringBuilder = new StringBuilder("[");
        for (int i = 0; i < usedSize - 1; i++) {
            stringBuilder.append(elem[i]);
            stringBuilder.append(",");
        }
        stringBuilder.append(elem[usedSize-1]);
        stringBuilder.append("]");
        return stringBuilder.toString();
    }
}

测试:

public static void main(String[] args) {
        myStackmyStack = new myStack<>();
        myStack.push(1);
        myStack.push(2);
        myStack.push(3);
        myStack.push(4);
        myStack.push(5);
        myStack.push(6);
        System.out.println(myStack);
        System.out.println(myStack.peek());
        System.out.println(myStack.pop());
        System.out.println(myStack.pop());
        System.out.println(myStack.pop());
        System.out.println(myStack.pop());

    }

结果:

Java-栈_第8张图片

 栈的介绍到此告一段落。

本文收录专栏《数据结构》。

 

你可能感兴趣的:(数据结构,Java学习,java,开发语言,后端)