数据结构-7.栈


前言

在这里插入图片描述

hello,大家好,我是阿旭啊。今天给大家带来数据结构第七篇:。希望能给大家一些小帮助~


一.栈的介绍

1.什么是栈

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守先进后出的原则 。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
出栈:栈的删除操作叫做出栈。出数据在栈顶
在这里插入图片描述

在这里插入图片描述
栈在现实中的例子:
在这里插入图片描述

2.栈的方法:

在这里插入图片描述
代码演示:

public static void main(String[] args) {
    System.out.println("1.=================================");
    Stack<Integer> stack = new Stack();
    //压栈
    stack.push(12);
    stack.push(23);
    stack.push(34);
    stack.push(45);
    System.out.println(stack);
    System.out.println("2.=================================");
    //出栈
    stack.pop();
    System.out.println(stack);
    //由此可见先入后出
    System.out.println("3.=================================");
    //获取栈顶数据,但是不出栈
    int a = stack.peek();
    System.out.println(a);
    System.out.println("4.=================================");
    //获取栈数据个数
    int size = stack.size();
    System.out.println(size);
    System.out.println("5.=================================");
    //判断是否栈空
    System.out.println(stack.empty());
    System.out.println("6.=================================");
}

运行结果:

1.=================================
[12, 23, 34, 45]
2.=================================
[12, 23, 34]
3.=================================
34
4.=================================
3
5.=================================
false
6.=================================

3.栈的模拟

在java原码中栈的底层是一个数组,所以我们可以使用数组进行站的模拟,当然也可以使用链表
这里我们使用数组进行模拟。
栈类:

public class MyStack {
    private int[] elem;
    private int usedSize;
    public MyStack(){
        this.elem = new int[10];
    }
    public void show(){
        for (int i = 0; i < this.usedSize; i++){
            System.out.print(this.elem[i] + " ");
        }
        System.out.println();
    }
    //1.压栈
    public void push(int value){
        if (this.elem.length == this.usedSize){
            this.elem = Arrays.copyOf(this.elem, this.usedSize * 2);
        }
        this.elem[usedSize] = value;
        usedSize ++;
    }
    //2.判空
    public boolean empty(){
        return this.usedSize == 0;
    }
    //3.出栈
    public int pop(){
        if (empty()){
            throw new StackEmptyException("栈为空!!!");
        }
        int a = this.elem[this.usedSize -1];
        usedSize--;
        return a;
    }
    //4.获取栈顶数据
    public int peek(){
        if (empty()){
            throw new StackEmptyException("栈为空!!!");
        }
        return this.elem[this.usedSize - 1];
    }
    //5.获取栈的数据个数
    public int size(){
        return this.usedSize;
    }
}

如果大家把顺序表搞明白了还是非常简单的!!!
自定义异常:

public class StackEmptyException extends RuntimeException{
    public StackEmptyException(){
    }
    public StackEmptyException(String message){
        super(message);
    }
}

代码测试:

public static void main(String[] args) {
    System.out.println("1.=================================");
    MyStack stack = new MyStack();
    //压栈
    stack.push(12);
    stack.push(23);
    stack.push(34);
    stack.push(45);
    stack.show();
    System.out.println("2.=================================");
    //出栈
    System.out.println(stack.pop());
    stack.show();
    //由此可见先入后出
    System.out.println("3.=================================");
    //获取栈顶数据,但是不出栈
    System.out.println(stack.peek());
    stack.show();
    System.out.println("4.=================================");
    //获取栈数据个数
    System.out.println(stack.size());
    System.out.println("5.=================================");
    //判断是否栈空
    System.out.println(stack.empty());
    System.out.println("6.=================================");
}

测试结果:

1.=================================
12 23 34 45 
2.=================================
45
12 23 34 
3.=================================
34
12 23 34 
4.=================================
3
5.=================================
false
6.=================================

二.栈的使用场景

1.链表逆序打印

(1)递归方法一:

public void show1(ListNode head){
    if (head != null){
        show1(head.next);
        System.out.print(head.value + " ");
    }
}

(2)递归方法二:

public void show2(ListNode head){
    if (head == null){
        return;
    }
    if (head.next == null){
        System.out.println(head.value);
        return;
    }
    show2(head.next);
    System.out.println(head.value);
}

(3)循环方法:

public void show3(ListNode head){
    Stack<ListNode> stack = new Stack<>();
    ListNode cur = head;
    //将节点放到栈中
    while(cur != null){
        stack.push(cur);
        cur = cur.next;
    }
    //将节点一个一个出栈
    while (!stack.empty()){
        ListNode node = stack.pop();
        System.out.println(node.value);
    }
}

2.逆波兰表达式

首先要了解中缀表达式和后缀表达式。
在这里插入图片描述
后缀表达式就是把符号放到数字的后面
将中缀表达式转换成后缀表达式:
如:a+bc+(de+f)g
在这里插入图片描述
将后缀表达式转换成中缀表达式:
如abc
+def+g+
思路:将每个字符放到栈中,若为运算符则弹出两个数据分别为右操作数和左操作数,然后将算出来的数据再放到栈中,直到所有字符都弹出过一次

在这里插入图片描述
逆波兰表达式就是后缀表达式,本题是输入一个逆波兰表达式计算其值。
思路:利用栈进行计算。遍历数组,如果字符是数字类型就转换成int类型然后压栈,如果是运算符则弹出两个栈中数据进行相应的计算(注意左右操作数顺序要对。)然后在进行压栈。计算完毕后返回栈中的数据(此时也只有一个数据了。)

public int evalRPN(String[] tokens) {
    Stack<Integer> stack = new Stack<>();
    for (int i = 0; i < tokens.length; i++) {
        if (tokens[i].equals("+") || tokens[i].equals("-") || tokens[i].equals("*") || tokens[i].equals("/")) {
            int num2 = stack.pop();
            int num1 = stack.pop();
            switch (tokens[i]) {
                case "+":
                    stack.push(num1 + num2);
                    break;
                case "-":
                    stack.push(num1 - num2);
                    break;
                case "*":
                    stack.push(num1 * num2);
                    break;
                case "/":
                    stack.push(num1 / num2);
                    break;
            }
        } else {
            stack.push(Integer.parseInt(tokens[i]));
        }
    }
    return stack.pop();
}

3.括号匹配问题

思路:如果是左括号则放到栈中,如果是有括号将栈顶元素弹出并判断匹配。
第一种情况:(){}[] 完美匹配
第二种情况:([)] 不匹配
第三种情况:(() 匹配但是字符串遍历完了,栈还没空
第四种情况:()) 匹配但是栈空了,字符串没有遍历完
只有第一种情况为true,其余情况为false
请看代码:

    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        //遍历字符串
        for (int i = 0; i < s.length(); i++){
            //判断是否为左括号
            if (s.charAt(i) == '{' || s.charAt(i) == '[' || s.charAt(i) == '('){
                //左括号压栈
                stack.push(s.charAt(i));
            }else{
                //判断是否栈空了但是字符串没有遍历完
                if (stack.empty()){
                    return false;
                }
                //匹配有括号
                char a = stack.pop();
                switch(s.charAt(i)){
                    case '}':
                        if (a != '{'){
                            return false;
                        }
                        break;
                    case ']':
                        if (a != '['){
                            return false;
                        }
                        break;
                     case ')':
                        if (a != '('){
                            return false;
                        }
                        break;
                } 
            }
        }
        //判断是否字符串遍历完,栈还没空
        if (!stack.empty()){
            return false;
        }
        return true;
    }

4.出栈入栈次序匹配

在这里插入图片描述
我们可以设置一个真实栈和一个最小值栈,最小栈的栈顶元素是真实栈的元素的最小值。
push方法:一开始两个栈都要压栈。真实栈直接压,当最小栈为空时也直接压。当最小栈不为空的时候,如果最小栈的栈顶元素>=压栈元素时,最小值栈也压栈,其他情况不压栈。

  public void push(int val) {
        stack.push(val);
        if (minstack.empty()){
            minstack.push(val);
        }else{
            if (val <= minstack.peek()){
                minstack.push(val);
            }
        }
    }

pop方法:首先判空,如果真实栈空就直接返回。真实栈直接弹出。当弹出元素与最小栈的栈顶元素相等时候,最小栈也弹出

    public void pop() {
        if (stack.empty()){
            return;
        }
        int val = stack.pop();
        if (val == minstack.peek()){
            minstack.pop();
        }
    }

全部代码:

class MinStack {
    private Stack<Integer> stack;
    private Stack<Integer> minstack;
    public MinStack() {
        this.stack = new Stack<>();
        this.minstack = new Stack<>();
    }
    
    public void push(int val) {
        stack.push(val);
        if (minstack.empty()){
            minstack.push(val);
        }else{
            if (val <= minstack.peek()){
                minstack.push(val);
            }
        }
    }
    
        public void pop() {
            if (stack.empty()){
                return;
            }
            int val = stack.pop();
            if (val == minstack.peek()){
                minstack.pop();
            }
        }
    
    public int top() {
        if (stack.empty()){
            return -1;
        }
        return stack.peek();
    }
    
    public int getMin() {
        return minstack.peek();
    }
}


总结

好啦~本篇文章就到此结束。希望大家能够多多点赞,多多评论,多多批评,大家下一篇再见!!!
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值