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

前言:本文结构
1.什么是栈
2.如何实现栈--->jdk如何实现栈
3.栈的应用
4.栈常见的算法题------>单调栈
5.未完待续
一切解决问题思路:栈先入后出的数据结构

1.什么是栈?

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

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

2.如何实现栈?

2.1对列实现栈
图解:

方案一:

代码:

class MyStack {
    Queue queue;

   
    public MyStack() {
        queue = new LinkedList();
    }
    
 
    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 queue1;//主队列
    Queue queue2;//辅助队列

    public MyStack() {
        queue1 = new LinkedList();
        queue2 = new LinkedList();
    }
    
   
    public void push(int x) {
        //元素插入辅助队列
        queue2.offer(x);
        //主队列元素插入辅助队列
        while (!queue1.isEmpty()) {
            queue2.offer(queue1.poll());
        }
        //主辅交换,辅助队列变为空
        Queue 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 extends Vector {
    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) {
        Stackstack = 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();
    }
}

方法二:辅助栈+HashMap

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

        Map pairs = new HashMap() {{
            put(')', '(');
            put(']', '[');
            put('}', '{');
        }};
        Deque stack = new LinkedList();
        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 stack = new LinkedList();
        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;i0&&heights[left-1]>=cur){
                left--;
            }
            while (right=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 stack=new LinkedList<>();
        stack.addLast(0);
        for (int i=1;i

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

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

补充:232. 用栈实现队列

    225. 用队列实现栈

5.未完待续

你可能感兴趣的:(java数据结构与算法栈面试)