leetcode面试题之栈与队列

232. 用栈实现队列

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

链接:https://leetcode.cn/problems/implement-queue-using-stacks/

思路:

用两个栈模拟队列
s1出队栈、s2入队栈

class MyQueue {
    Stack<Integer> s1, s2;
    public MyQueue() {
        s1 = new Stack<>(); // 模拟出队栈
        s2 = new Stack<>(); // 模拟入队栈
    }
    public void push(int x) {
        s2.push(x);
    }
    // 出栈时,如果s1为空,s2不为空,将s2中的数全部放入s1,再出栈;s1不为空则直接出栈
    public int pop() {
        if (s1.isEmpty()) {
            while (!s2.isEmpty()) {
                s1.push(s2.pop());
            }
        }
        return s1.pop();
    }
    // 如果s1为空,s2不为空,则把s2中的数全部放入s1,再获取栈顶元素
    public int peek() {
        if (s1.isEmpty()) {
            while (!s2.isEmpty()) {
                s1.push(s2.pop());
            }
        }
        return s1.peek();
    }
    public boolean empty() {
        return s1.isEmpty() && s2.isEmpty();
    }
}

225. 用队列实现栈

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

链接:https://leetcode.cn/problems/implement-stack-using-queues/

思路:用一个队列模拟栈,每次出栈or取栈顶元素时,将队中元素全部出队再入队,以不同的方式处理尾元素

class MyStack {
    // 用一个队列模拟栈
    Queue<Integer> q;
    public MyStack() {
        q = new LinkedList<>();
    }
    public void push(int x) {
        q.add(x);
    }
    // 出队列,并把 除最后一个元素外 的其他元素重新放入队列
    public int pop() {
        int size = q.size();
        size --;
        while (size-- > 0) {
            q.offer(q.poll());
        }
        return q.poll();
    }
    // 出队列,把所有元素重新放入队列,当最后一个元素时,记录一下作为返回值
    public int top() {
        int size = q.size();
        while (size-- > 1) {
            q.offer(q.poll());
        }
        int res = q.poll();
        q.offer(res);
        return res;
    }
    public boolean empty() {
        return q.isEmpty();
    }
}

20. 有效的括号

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

链接:https://leetcode.cn/problems/valid-parentheses/

思路:栈
遇到左括号入栈,右括号出栈比对,最后栈为空即为匹配成功

public boolean isValid(String s) {
        Stack<Character> st = new Stack<>();
        int n = s.length();
        for (int i = 0; i < n; i ++) {
            char c = s.charAt(i);
            if (c == '(') {
                st.push(')');
            } else if (c == '{') {
                st.push('}');
            } else if (c == '[') {
                st.push(']');
            } else if (st.isEmpty() || c != st.peek()){
                return false;
            } else {
                st.pop();
            }
        }
        return st.isEmpty();
    }

1047. 删除字符串中的所有相邻重复项

题目:给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

链接:https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/

思路:

用数组模拟栈,不用真正的栈!!
用一个下标模拟栈的栈顶指针
入栈情况:1)栈为空 2)栈不为空时,栈顶元素与遍历的字符不相等;否则出栈

public String removeDuplicates(String s) {
        // 直接用字符串模拟栈,不用真正的栈
        int top = -1; // 栈顶指针
        char[] ch = s.toCharArray();
        for (int i = 0; i < ch.length; i ++) {
            // 入栈情况:1)栈为空 2)栈不为空时,栈顶元素与遍历的字符不相等;否则出栈
            if (top < 0 || ch[i] != ch[top]) {
                ch[++ top] = ch[i];
            } else {
                top --;
            }
        }
        return String.valueOf(ch, 0, top + 1);
    }

150. 逆波兰表达式求值

题目:根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

注意 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

链接:https://leetcode.cn/problems/evaluate-reverse-polish-notation/

思路:逆波兰表达式:用数组模拟栈
特点:操作数的个数一定比运算符的个数多 1 个,即包含最多(n+1)/2个操作数,(n-1)/2个操作符

public int evalRPN(String[] tokens) {
        // 数组模拟栈
        int top = -1, n = tokens.length;
        // 注意:操作数个数比操作运算符至少多一个,即最多包含(n+1)/2个操作数
        int[] st = new int[(n+1)/2]; 
        for (String i : tokens) {
            if ("+".equals(i) || "-".equals(i) || "*".equals(i) || "/".equals(i)) {
                int num1 = st[top --], num2 = st[top --];
                if ("+".equals(i)) st[++ top] =  num2 + num1;
                if ("-".equals(i)) st[++ top] =  num2 - num1;
                if ("*".equals(i)) st[++ top] =  num2 * num1;
                if ("/".equals(i)) st[++ top] =  num2 / num1;
            } else {
                st[++ top] = Integer.valueOf(i); // 字符串转为整型之后在入栈
            }
        }
        return st[top];
    }

239. 滑动窗口最大值

题目:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值 。

链接:https://leetcode.cn/problems/sliding-window-maximum/

思路:单调双端队列
(一般用到单调队列思想都是双端队列)

public int[] maxSlidingWindow(int[] nums, int k) {
        // 思路:单调双端队列
        int n = nums.length;
        int[] res = new int[n - k + 1];
        Deque<Integer> q = new ArrayDeque<>(); // 双端队列(存放的是下标)
        for (int i = 0, idx = 0; i < n; i ++) {
            // 新来一个数,移除一个旧数
            if (!q.isEmpty() && i - k + 1 > q.peekFirst()) q.removeFirst();
            // 新来的数>队尾的数,删除队尾的数,直到新来的数<=队尾的树
            while (!q.isEmpty() && nums[i] > nums[q.peekLast()]) q.removeLast();
            // 当前数下标入队
            q.addLast(i);
            // 每遍历一次,队头的下标就是最大值的下标
            // i从k-1的位置开始才输出队列最大值
            if (i >= k - 1) res[idx ++] = nums[q.peekFirst()];
        }
        return res;
    }

347. 前 K 个高频元素

题目:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

链接:https://leetcode.cn/problems/top-k-frequent-elements/

思路:

  1. map统计频次
  2. 计数排序法
    (1)统计出现i次的有几个整数
    (2)找分界点,从后往前遍历n–,次数i求和直到k
    (3)遍历哈希表,次数超过n的记录答案
public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        // 使用map,统计每个整数出现的次数
        for (int i : nums) {
            // if (map.containsKey(i)) {
            //     map.put(i, map.get(i)+1)
            // } else {
            //     map.put(i, 1);
            // }
            map.put(i, map.getOrDefault(i, 0) + 1);
        }
        // 计数排序算法
        int n = nums.length;
        int[] cnt = new int[n + 1];
        for (int i : map.values()) {
            cnt[i] ++; // 出现i次的元素有cnt[i]个
        }
        // 找分界点,从后往前遍历,直到下标凑到k
        int m = 0;
        while (m != k) {
            m += cnt[n --];
        }
        int[] res = new int[k];
        m = 0;
        // 遍历哈希表,如果次数超过n,记录答案
        for(int i: map.keySet()){
            if(map.get(i) > n){
                res[m ++] = i;
            }
        }
        return res;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值