LeetCode - 5. 栈与队列

刷题顺序来自:代码随想录

用栈实现队列

232. 用栈实现队列

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

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 `false
class MyQueue {
    Stack<Integer> pushStack = new Stack<>();
    Stack<Integer> popStack = new Stack<>();

    public MyQueue() {
    }
    
    public void push(int x) {
        pushStack.push(x);
    }
    
    public int pop() {
        if(popStack.empty()) {
            move(pushStack, popStack);
        }
        return popStack.pop();
    }
    
    public int peek() {
        if(popStack.empty()) {
            move(pushStack, popStack);
        }
        return popStack.peek();        
    }
    
    public boolean empty() {
        return pushStack.empty() && popStack.empty();
    }
	
    // 将栈1的元素都移动到栈2
    private void move(Stack<Integer> s1, Stack<Integer> s2) {
        while(!s1.empty()) {
            int element =  s1.pop();
            s2.push(element);
        }
    }
}

225. 用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppopempty)。

可以仅用一个队列实现。

class MyStack {
    // Queue是接口,需要用其实现类实例化
    Queue<Integer> queue = new LinkedList<Integer>();

    public MyStack() {
    }
    
    public void push(int x) {
        queue.offer(x);
    }
    
    public int pop() {
        move();
        return queue.poll();
    }
    
    public int top() {
        move();
        int top = queue.peek();
        queue.offer(queue.poll());
        return top;
    }
    
    public boolean empty() {
        return queue.size() == 0;
    }
	
    // 取出队列的前n-1个元素,放入队尾
    // 此时队列头元素为原来的最后一个元素
    private void move() {
        for(int i = 0; i < queue.size() - 1; i++) {
            queue.offer(queue.poll());
        }
    }
}

有效的括号

20. 有效的括号

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

示例 1:

输入:s = "{[]}"
输出:true

示例 2:

输入:s = "([)]"
输出:false

使用栈匹配。

public boolean isValid(String s) {
    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()) {
                return false;
            }
            char pop = stack.pop();
            if((ch == ')' && pop != '(') || (ch == ']' && pop != '[') || (ch == '}' && pop != '{')) {
                return false;
            }
        }
    }

    return stack.empty();
}

删除重复相邻项

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

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

示例 1:

输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。

当前字符和栈顶字符不相同,当前字符入栈,否则栈顶字符出栈。

public String removeDuplicates(String s) {
    LinkedList<Character> list = new LinkedList<>();

    for(int i = 0; i < s.length(); i++) {
        char curr = s.charAt(i);
        if(list.size() == 0 || list.peekLast() != curr) {
            list.offerLast(curr);
        }
        else {
            list.pollLast();
        }
    }

    StringBuilder res = new StringBuilder();
    while(list.size() != 0) {
        res.append(list.pollFirst());
    }

    return res.toString();
}

逆波兰表达式求值

150. 逆波兰表达式求值

有效的算符包括 +-*/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

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

示例 1:

输入:s = "{[]}"
输出:true

示例 2:

输入:s = "{[]}"
输出:true

示例 3:

输入:s = "{[]}"
输出:true
public int evalRPN(String[] tokens) {
    Stack<Integer> nums = new Stack<>();

    for(String s: tokens) {
        if (s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")) {
            int num1 = nums.pop();
            int num2 = nums.pop();
            nums.push(calculate(num2, num1, s));
        }
        else {
            nums.push(Integer.parseInt(s));
        }          
    }

    return nums.pop();
}

// int a b 的计算值
public int calculate(int a, int b, String op) {
    if (op.equals("+")) {
        return a + b;
    }
    else if (op.equals("-")) {
        return a - b;
    }
    else if (op.equals("*")) {
        return a * b;
    }
    else if (op.equals("/")) {
        return a / b;
    }
    return 0;
}

滑动窗口最大值

239. 滑动窗口最大值

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

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

示例 2:

输入:nums = [1], k = 1
输出:[1]

示例 3

输入:nums = [1,-1], k = 1
输出:[1,-1]

维护一个有序的Deque,队列中元素值递减,每次滑动窗口后,移除旧元素,加入新元素,队列第一个元素就是最大值。然而实际上不需要维护窗口中的所有元素

在每次将新元素添加到队列尾部时,需要移除队列尾部值比新元素更小的元素。这是为了保证每次添加的新元素是队列中的最小值,我们可以永久移除队列中比新元素值小的老元素的原因是:

  • 新元素的索引是队列中最大的,队列中有元素比新元素的值还要小时,它永远都不可能成为最大值,因为它会被新元素更早被滑出窗口
public int[] maxSlidingWindow(int[] nums, int k) {
    if(nums.length <= 1 || k <= 1) return nums;

    int[] res = new int[nums.length - k + 1];
    LinkedList<Integer> list = new LinkedList<>();

    for(int i = 0; i < k; i++) {
        // 添加新元素到队列尾部
        // 需要保证队列所有元素都是不小于新元素的
        while(list.size() != 0 && list.getLast() < nums[i]) {
            list.removeLast();
        }
        list.add(nums[i]);
    }
    res[0] = list.getFirst();

    for(int i = k; i < nums.length; i++) {
        // 滑动窗口右移,元素nums[i-k]被移出窗口,需要判断其是否是最大值
        if(list.getFirst() == nums[i-k]){
            list.removeFirst();
        }

        while(list.size() != 0 && list.getLast() < nums[i]) {
            list.removeLast();
        }
        list.add(nums[i]);

        res[i-k+1] = list.getFirst();
    }

    return res;
}

前k个高频元素

347. 前 K 个高频元素

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

public int[] topKFrequent(int[] nums, int k) {
    // 通过HashMap统计每个整数出现的频率
    HashMap<Integer, Integer> map = new HashMap<>();
    for(int i: nums) {
        map.put(i, map.getOrDefault(i, 0) + 1);
    }

    // 将HashMap中的Entry存储到list数组中
    Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
    ArrayList<Map.Entry<Integer, Integer>> list = new ArrayList<>();
    for(Map.Entry<Integer, Integer> entry: entries) {
        list.add(entry);
    }

    // 根据索引进行排序
    list.sort(new Comparator<Map.Entry<Integer, Integer>>() {
        public int compare(Map.Entry<Integer, Integer> e1,  Map.Entry<Integer, Integer> e2) {
            return e1.getValue().compareTo(e2.getValue());
        }
    }
             );

    // 取出现频率前k高的整数
    int[] res = new int[k];
    for(int i = 0; i < k; i++) {
        res[i] = list.get(list.size() - i - 1).getKey();
    }

    return res;
}
posted @ 2021-11-28 01:20  lv6laserlotus  阅读(29)  评论(0)    收藏  举报