【数据结构】 栈 常考 OJ面试题

栈的常考 OJ面试问题

一、最小栈

oj链接

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

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

📜📜大体思路:

在栈中要找到最小元素我们可以借助两个栈,一个是普通栈(stack),另一个是存储最小值的栈(minStack),我们每放一个值在栈中如果是栈中最小的,我们就去记录这个值然后放入minStack栈中,相同元素也要放入栈中,元素弹出的时候要两个栈里面的相同元素也要弹出,最后可以获得栈顶元素,并检索栈中的最小元素。

image-20220211205753413

💡💡💡核心代码:


public void push(int val) {
        stack.push(val);
        if(minStack.empty()) {
            minStack.push(val);
        } else {
            int x = minStack.peek();
            if (val <= x) { // = 必须加,不加出bug
                minStack.push(val);
            }
        }
    }
    public void pop() {
        int x = stack.pop();
        if(x == minStack.peek()) {
            minStack.pop();
        }
    }
  • 完成代码:
class MinStack {
    private Stack<Integer> stack;
    private Stack<Integer> minStack;
    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }
    public void push(int val) {
        stack.push(val);
        if(minStack.empty()) {
            minStack.push(val);
        } else {
            int x = minStack.peek();
            if (val <= x) { //= 必须加
                minStack.push(val);
            }
        }
    }
    public void pop() {
        int x = stack.pop();
        if(x == minStack.peek()) {
            minStack.pop();
        }
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

二、有效的括号

oj 链接

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

有效字符串需满足:

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

📜📜大体思路:

我们需要建立一个栈,然后遍历字符串,把左边所有的括号全放进栈里面,遍历的右边的括号的时候,

1、如果遍历到的括号与栈里面的括号对应,那么弹出栈,并且栈和字符串都遍历完,返回true;

2、如果左括号多,当字符串遍历完成的时候,那么栈是不空的,返回false。

3、如果右括号多:当字符串遍历到右括号的时候,此时去栈内找左括号,发现栈是空,返回false。

image-20220211211822304

💡💡💡完成代码:

class Solution {
    public boolean isValid(String s) {
        if(s.length() == 0) return true;
        if(s == null) return false;

        Stack<Character> stack=new Stack<>();
        for(int i=0;i<s.length();i++) {
            char ch=s.charAt(i);
            if(ch=='(' || ch=='{'|| ch=='[') {
            stack.push(ch);
            } else {
            if(stack.empty()) {
                System.out.println("右括号多");
                return false;
            }
                //栈不空,检查栈顶,弹出
                char tmp=stack.peek();
                if(tmp=='(' && ch==')' || tmp=='['&&ch==']'|| tmp=='{' && ch=='}') {
                    stack.pop();
                
                } else {
                    System.out.println("左右不匹配");
                    return false;
                }
            }
        }
        if(!stack.empty())  {
            System.out.println("左括号多");
            return false;
        }
        return true;
    }
}

三、用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和
empty)。实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

OJ链接

📜📜大体思路:

用队列实现栈,首先必须需要两个队列,如果两个队列都为空,放元素就先放到qu1里面,之后还要添加元素那么就是往队列不为空的队列里面放元素。那么你要弹出一个后入先出的一个栈元素,就要把这个队列里面size-1个元素放入另一个队列中,最后一个元素弹出即可,之后循环反复。

image-20220212114454923

💡💡💡核心代码:

public int pop() {
        if(empty()) {
            return -1;
        }
        int e = -1;
        if(!qu1.isEmpty()) {
            int size = qu1.size();
            for(int i = 0;i < size-1;i++){
                e = qu1.poll();
                qu2.offer(e);
            }
            e = qu1.poll();//这是最后弹出的元素
        }else{
            int size = qu2.size();
            for(int i = 0;i < size-1;i++){
                e = qu2.poll();
                qu1.offer(e);
            }
            e = qu2.poll();
        }
        return e;//返回e实现一个后入先出的栈
    }
  • 完整代码
class MyStack {
    private Queue<Integer> qu1;
    private Queue<Integer> qu2;

    public MyStack() {
        qu1=new LinkedList<>();
        qu2=new LinkedList<>();
    }
    
    public void push(int x) {
        if(!qu1.isEmpty()){
            qu1.offer(x);
        }else if(!qu2.isEmpty()){
            qu2.offer(x);
        }else{
            qu1.offer(x);
        }
    }
    
    public int pop() {
        if(empty()) {
            return -1;
        }
        int e = -1;
        if(!qu1.isEmpty()) {
            int size = qu1.size();
            for(int i = 0;i < size-1;i++){
                e = qu1.poll();
                qu2.offer(e);
            }
            e = qu1.poll();
        }else{
            int size = qu2.size();
            for(int i = 0;i < size-1;i++){
                e = qu2.poll();
                qu1.offer(e);
            }
            e = qu2.poll();
        }
        return e;
    }   
    public int top() {
        if(empty()) {
            return -1;
        }
        int e = -1;
        if(!qu1.isEmpty()) {
            int size = qu1.size();
            for(int i = 0;i < size;i++){
                e = qu1.poll();
                qu2.offer(e);
            }
        }else{
            int size = qu2.size();
            for(int i = 0;i < size;i++){
                e = qu2.poll();
                qu1.offer(e);
            }
        }
        return e;
    }
    //栈为空
    public boolean empty() {
        return qu1.isEmpty() && qu2.isEmpty();
    }
}

四、用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

OJ链接

📜📜大体思路:

用栈实现队列,首先必须需要两个栈,队列是先进先出,所有我们用一个栈放入数据,从这个栈里面弹出再放入另一个栈,最后从另一个栈弹出就是实现队列,中途如果要加元素,也是先放入第一个栈里面,然后另一个栈弹出完后再放入另一个栈然后弹出。

image-20220212145133083

💡💡💡核心代码:

public int pop() {
        if(empty()) return -1;
        if(s2.empty()) {
            while(!s1.empty()) {
                s2.push(s1.pop());
            }
        }
        return s2.pop();
    }
  • 完整代码:
class MyQueue {
    private Stack<Integer> s1;
    private Stack<Integer> s2;

    public MyQueue() {
        s1 = new Stack<>();
        s2 = new Stack<>();
    }
    
    public void push(int x) {
        s1.push(x);//指定放在s1;
    }
    
    public int pop() {
        if(empty()) return -1;
        if(s2.empty()) {
            while(!s1.empty()) {
                s2.push(s1.pop());
            }
        }
        return s2.pop();
    }
    
    public int peek() {
        if(empty()) return -1;
        if(s2.empty()) {
            while(!s1.empty()) {
                s2.push(s1.pop());
            }
        }
        return s2.peek();
    }
    
    public boolean empty() {
        return s1.empty() && s2.empty();
    }
}

五、设计循环队列

OJ 链接

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。

📜📜大体思路:

如果使用数组实现循环队列,如果一组数大于这个数组的长度那该怎么办?,可以使用覆盖的方法,前面的元素向前移动,出队列后,后面的在覆盖,但是时间复杂度就是O(n)。

image-20220227212650013

那么既然是循环队列,那么循环两字就可以想到圆,我们可以把一组数据放进圆中,下标也放进去。

image-20220227212922574

一个圆里面没满的情况下,直接出就行,那么一个圆放满了过后,怎么循环出尼?
1、首先设置两个变量,front,rear;
> front指向队列的头部
> rear指向队列的尾部

image-20220227213838182

从图可知:
1、front和rear相遇是满还是空尼?
2、front怎么从7小标走到0小标?
(1)、规定相遇为null
>首先可以浪费一个格子,rear下一个front 就是 满
(2)、利用公式:(rear+1)%length

image-20220227214419435

💡💡💡完整代码:

class MyCircularQueue {

   private int[] elem;
    private int front;
    private int rear;


    public MyCircularQueue(int k) {
        this.elem=new int[k+1];
    }

    //入队
    public boolean enQueue(int value) {
        if(isFull()) {
            //不需要扩容
            return false;
        }
        this.elem[this.rear]=value;
        this.rear=(this.rear+1)% elem.length;
        return true;
    }

    public boolean isFull() {
       if((this.rear+1)% elem.length==this.front) {
           return true;
       }
       return false;
    }

    //出队列
    public boolean deQueue() {
        if(isEmpty()) {
            return false;
        }
        this.front=(this.front+1)% elem.length;
        return true;
    }

    public boolean isEmpty() {
        if(this.front==this.rear) {
            return true;
        }
        return false;
    }
    
    //得到队头元素  相当于 peek()
    public int Front() {
        if(isEmpty()) {
            return -1;
        }
        int val=this.elem[this.front];
        return val;
    }

    
    //得到队尾元素
    public int Rear() {
        if(isEmpty()) {
            return -1;
        }
        if(this.rear==0) {
            return this.elem[this.elem.length-1];
        }
        return this.elem[this.rear-1];
    }
}

铁汁们,觉得笔者写的不错的可以点个赞哟❤🧡💛💚💙💜🤎🖤🤍💟,收藏关注呗,你们支持就是我写博客最大的动力!!!!

  • 30
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 24
    评论
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鸢也

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值