栈(面试一锅端了系列之数据结构栈)

前言:本文结构
1.什么是栈
2.如何实现栈—>jdk如何实现栈
3.栈的应用
4.栈常见的算法题------>单调栈

5.未完待续
一切解决问题思路:栈先入后出的数据结构

1.什么是栈?

1)栈(Stack)是一种先入后出的线性表
2)栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top)(我们在栈顶进行的插入和删除称为入栈和出栈)另一端为固定的一端,称为栈底(Bottom)。
3)根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除

在这里插入图片描述

栈像我们的水桶(我们加水和取水想入栈和出栈,然后也只能在有口的一端(栈顶)进行操作,封口的一端(栈底)无法操作)
在这里插入图片描述

2.如何实现栈?

2.1对列实现栈
图解:
在这里插入图片描述
方案一:
在这里插入图片描述

代码:

class MyStack {
    Queue<Integer> queue;

   
    public MyStack() {
        queue = new LinkedList<Integer>();
    }
    
 
    public void push(int x) {
        //算出插入前所有元素个数
        int n = queue.size();
        //插入新元素
        queue.offer(x);
      //把前面元素从队头取出,插入队尾  
          for (int i = 0; i < n; i++) {
            queue.offer(queue.poll());
        }
    }
    
    
    public int pop() {
        return queue.poll();
    }
    
  
    public int top() {
        return queue.peek();
    }
    
   
    public boolean empty() {
        return queue.isEmpty();
    }
}

方案二:
在这里插入图片描述

class MyStack {
    Queue<Integer> queue1;//主队列
    Queue<Integer> queue2;//辅助队列

    public MyStack() {
        queue1 = new LinkedList<Integer>();
        queue2 = new LinkedList<Integer>();
    }
    
   
    public void push(int x) {
        //元素插入辅助队列
        queue2.offer(x);
        //主队列元素插入辅助队列
        while (!queue1.isEmpty()) {
            queue2.offer(queue1.poll());
        }
        //主辅交换,辅助队列变为空
        Queue<Integer> temp = queue1;
        queue1 = queue2;
        queue2 = temp;
    }
    
  
    public int pop() {
        return queue1.poll();
    }
    
    /** Get the top element. */
    public int top() {
        return queue1.peek();
    }
    
    /** Returns whether the stack is empty. */
    public boolean empty() {
        return queue1.isEmpty();
    }
}

2.2:数组实现栈
在这里插入图片描述

class ArrayStack {
    private int maxSize; // 栈的大小
    private int[] stack; // 数组,数组模拟栈,数据就放在该数组
    private int top = -1;// top表示栈顶,初始化为-1

    //构造器
    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[this.maxSize];
    }

    //栈满
    public boolean isFull() {
        return top == maxSize - 1;
    }
    //栈空
    public boolean isEmpty() {
        return top == -1;
    }
    //入栈-push
    public void push(int value) {
        //先判断栈是否满
        if(isFull()) {
            throw new RuntimeException("栈满");
        }
        top++;
        stack[top] = value;
    }
    //出栈-pop, 将栈顶的数据返回
    public int pop() {
        //先判断栈是否空
        if(isEmpty()) {
            //抛出异常
            throw new RuntimeException("栈空,没有数据~");
        }
        int value = stack[top];
        top--;
        return value;
    }
    //显示栈的情况[遍历栈], 遍历时,需要从栈顶开始显示数据
    public void list() {
        if(isEmpty()) {
            System.out.println("栈空,没有数据~~");
            return;
        }
        //需要从栈顶开始显示数据
        for(int i = top; i >= 0 ; i--) {
            System.out.printf("stack[%d]=%d\n", i, stack[i]);
        }
    }

}

2.3:jdk中的Stack如何实现

1.主要是继承了Vector
2.push(),pop(),empty(),peek()操作,在方法上加了synchronized修饰,所以效率比较慢,然后被划为不推荐使用。

public class Stack<E> extends Vector<E> {
    private static final long serialVersionUID = 1224463164541339165L;

    public Stack() {
    }

    public E push(E item) {
        this.addElement(item);
        return item;
    }

    public synchronized E pop() {
        int len = this.size();
        E obj = this.peek();
        this.removeElementAt(len - 1);
        return obj;
    }

    public synchronized E peek() {
        int len = this.size();
        if (len == 0) {
            throw new EmptyStackException();
        } else {
            return this.elementAt(len - 1);
        }
    }

    public boolean empty() {
        return this.size() == 0;
    }

    public synchronized int search(Object o) {
        int i = this.lastIndexOf(o);
        return i >= 0 ? this.size() - i : -1;
    }
}

3:栈的应用?

1)函数的调用
比如:java虚拟机栈(虚拟机为每个线程创建一个私有的栈,每个方法的调用都会创建一个栈帧,每一个方法从调用到方法返回都对应着一个栈帧入栈出栈的过程。
2)递归调用-–>深度搜索(dfs)---->二叉树遍历(递归实现)
3)括号匹配
4)后缀表达式求值
等等等

4.栈常见的算法题(由简到难)

  1. 有效的括号
    方法一:辅助栈
class Solution {
    public boolean isValid(String s) {
        Stack<Character>stack = new Stack<Character>();
        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();
    }
}

方法二:辅助栈+HashMap

class Solution {
    public boolean isValid(String s) {
        int n = s.length();
        if (n % 2 == 1) {
            return false;
        }

        Map<Character, Character> pairs = new HashMap<Character, Character>() {{
            put(')', '(');
            put(']', '[');
            put('}', '{');
        }};
        Deque<Character> stack = new LinkedList<Character>();
        for (int i = 0; i < n; i++) {
            char ch = s.charAt(i);
            if (pairs.containsKey(ch)) {
                if (stack.isEmpty() || stack.peek() != pairs.get(ch)) {
                    return false;
                }
                stack.pop();
            } else {
                stack.push(ch);
            }
        }
        return stack.isEmpty();
    }
}
  1. 逆波兰表达式求值
class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList<Integer>();
        int n = tokens.length;
        for (int i = 0; i < n; i++) {
            String token = tokens[i];
            if (isNumber(token)) {
                stack.push(Integer.parseInt(token));
            } else {
                int num2 = stack.pop();
                int num1 = stack.pop();
                switch (token) {
                    case "+":
                        stack.push(num1 + num2);
                        break;
                    case "-":
                        stack.push(num1 - num2);
                        break;
                    case "*":
                        stack.push(num1 * num2);
                        break;
                    case "/":
                        stack.push(num1 / num2);
                        break;
                    default:
                }
            }
        }
        return stack.pop();
    }

    public boolean isNumber(String token) {
        return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
    }
}
  1. 柱状图中最大的矩形
    1.暴力法:超出时间限制,呜呜呜呜呜呜呜呜呜
class Solution {
    public int largestRectangleArea(int[] heights) {
     int len=heights.length;
     if (len==0){
         return 0;
     }
     int res=0;
     for (int i=0;i<len;i++){
         int left=i;
         int right=i;
            int cur=heights[i];
            //从右到左找到最后大于等于当前数的下标
            while(left>0&&heights[left-1]>=cur){
                left--;
            }
            while (right<len-1&&heights[right+1]>=cur){
                right++;
            }
            res= Math.max((right-left+1)*heights[i],res);
        }
        return res;
    }
}

2.单调栈

class Solution {
 //单调栈+哨兵节点(减少边界的判断)
    public int largestRectangleArea(int[] heights) {
        int res=0;
        int len=heights.length;
    //判断特殊情况
        if (len==0){
            return 0;
        }
        if (len==1){
            return heights[0];
        }
    //新建哨兵节点
        int[] newHeight=new int[len+2];
        newHeight[0]=0;
        System.arraycopy(heights,0,newHeight,1,len);
        newHeight[len+1]=0;
        len+=2;
//        用双端对列做为栈
        Deque<Integer> stack=new LinkedList<>();
        stack.addLast(0);
        for (int i=1;i<len;i++){
        while (newHeight[i]<newHeight[stack.peekLast()]){
            int curHigh=newHeight[stack.pollLast()];
            int curWidth=i-stack.peekLast()-1;
            res=Math.max(curHigh*curWidth,res);
        }
         stack.addLast(i);
        }
        return res;
    }
}

TOList
有了前面的基础,这不得迎刃而解,噢哈哈哈哈哈

  1. 每日温度
    1.暴力法
    2.单调栈
  2. 接雨水

补充:232. 用栈实现队列
225. 用队列实现栈

5.未完待续

小结尾

这里是和你一起成长的小傻子echo,后续会不断更新我准备面试的笔记
佛系求关注:
整理不易,要是觉得有用的话,点个赞,关注一哈我更开心啦
我搞了个技术公众号【Echo画java】,保证没广告,欢迎找我聊天
(嘿嘿,想体验一波被关注的感觉,现在首要任务认真复习,整理基础和项目,准备好秋招)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值