代码随想录Day11

这里写目录标题

  • 逆波兰表达式求值
  • 滑动窗口最大值
  • 前 K 个高频元素
  • 接雨水

逆波兰表达式求值

lc150. 逆波兰表达式求值(中等)

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。

注意:

  1. 有效的算符为 ‘+’、‘-’、‘*’ 和 ‘/’ 。
  2. 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  3. 两个整数之间的除法总是 向零截断 。
  4. 表达式中不含除零运算。
  5. 输入是一个根据逆波兰表示法表示的算术表达式。
  6. 答案及所有中间计算结果可以用 32 位 整数表示。

代码随想录Day11_第1张图片

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList();
        for (String token : tokens) {
            char c = token.charAt(0);
            if (!isOpe(token)) {
                stack.addFirst(stoi(token));
            }else if (c == '+') {
                stack.push(stack.pop() + stack.pop());
            }else if (c == '-') {
                int num1 = stack.pop();
                int num2 = stack.pop();
                stack.push(num2 - num1);
            }else if (c == '*') {
                stack.push(stack.pop() * stack.pop());
            }else {
                int num1 = stack.pop();
                int num2 = stack.pop();
                stack.push(num2 / num1);
            }
        }
        return stack.pop();
    }
    private boolean isOpe(String s) {
        return s.length() == 1 && s.charAt(0) < '0' || s.charAt(0) > '9';
    }
    private int stoi(String s) {
        return Integer.valueOf(s);
    }
}

逆波兰式是比较典型的使用栈来解决的问题,这思路比较容易想到,但是题中还是有几处容易出错:

  1. 首先我们要知道,将字符串转化成Intege类型才能操作算术运算。
  2. String类型的字符串可以使用增强for来进行遍历,每次遍历一个字符。
  3. 什么情况下入栈(不是单纯的不等于±*/),而是需要判定长度为1,数字为1到9时入栈。
  4. 减法和除法需要注意运算顺序。
  5. Deque和Stack的区别到底是什么。
    Deuqe,即“double ended queue”的缩写,是Java中的双端队列集合类型。
    具备普通队列FIFO的功能,也具备了Stack的LIFO功能。
    Deque可以由ArrayDeuqe或者LinkedList实现。
    Vector 很多方法都用了synchronized修饰,线程安全,但效率太低,已被弃用。
    Stack 继承Vector,暴露了set/get方法,可以进行随机位置的访问,与与Stack 的设计理念相冲突

滑动窗口最大值

lc239. 滑动窗口最大值(困难)

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

代码随想录Day11_第2张图片

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums.length == 1) {
            return nums;
        }
        MyQueue que = new MyQueue();
        int len = nums.length;
        int index = 0;
        int[] result = new int[len - k + 1];
        for (int i = 0; i < k; i++) {
            que.push(nums[i]);
        }
        result[index++] = que.front();
        for (int i = k; i < len; i++) {
            que.pop(nums[i - k]);
            que.push(nums[i]);
            result[index++] = que.front();
        }
        return result;
    }
}
public class MyQueue {
    // 使用Deque
    Deque<Integer> que = new LinkedList<>();
    // 定义出队列操作
    //弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
    //同时判断队列当前是否为空
    public void pop (int value) {
        if (!que.isEmpty() && value == que.peek()) {
            que.poll();
        }
    }
    // 定义进队操作
    //添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
    //保证队列元素单调递减
    //比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
    public void push(int value) {
        while (!que.isEmpty() && value > que.getLast()) {
            que.removeLast();
        }
        que.add(value);
    }
    // 取出队列最前面的值,但是不弹出
    public int front() {
        return que.peek();
    }
}

这道题被定义为一个困难题,但是仔细分析后, 是可以解决的。
需要注意的点:

  1. 使用Deque的队列,并且使用实现这个接口的类LinkedList来实现。
  2. 方法的使用,这里面方法实在是太多了,怎么区分与选择?
  3. peek()、poll()、removeLast()

了解我们的单调队列:

我们需要自己造一个单调队列,队列里面的元素需要单调递减,存放的是我们一个滑动窗口中的元素的一个单调递减顺序,目前主要的注意力就是在进队,如果发现进队的元素大于队列队尾元素,则就尾元素出队,则直到进队这个元素的地方,前面窗口的中元素都小于该元素,所以可以将对列清空,在入队该元素。
代码随想录Day11_第3张图片

出队操作,考虑到什么时候什么时候需要出队,就是该队首的元素已经滑到窗口外了,不属于滑动窗口中最大的元素了。


前 K 个高频元素

lc347. 前 K 个高频元素(中等)

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

代码随想录Day11_第4张图片

此题基本上就是全懵状态了:

  1. 不熟悉map的使用,特别是entrySet()这个方法的返回结果。
  2. 没有使用过堆来解决问题,堆的原理是懂得,就是api不熟悉。
  3. PriorityQueue不熟悉,包括其中的方法不熟悉。
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        int[] result = new int[k];
        // 使用HashMap来统计每个元素出现的次数
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
         // 根据map的value值正序排,相当于一个小顶堆
         PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());
         for (Map.Entry<Integer, Integer> entry : entries) {
            queue.offer(entry);
            if (queue.size() > k) {
                queue.poll();
            }
        }
        for (int i = k - 1; i >= 0; i--) {
            result[i] = queue.poll().getKey();
        }
        return result;
    }
}

该题基本上是属于抄的一个状态,但是我们要清楚的是什么时候需要使用堆这种方法,并且合理的选择大顶堆和小顶堆。PriorityQueue就是实现堆的一种操作我们也需要了解。


接雨水

lc42. 接雨水(困难)

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

代码随想录Day11_第5张图片

此题解法比较多,目前就实现了双指针,然后还可以使用动规、单调栈来解题,个人认为双指针解法完全在于逻辑问题,是达不到困难的程度的。

class Solution {
    public int trap(int[] height) {
        int res = 0;
        for (int i = 0; i < height.length; i++) {
            // 第一个和最后一个必没有存水,所以不计算
            if (i == 0 || i == height.length - 1) {
                continue;
            }
            int lheight = height[i];
            int rheight = height[i];
            // 寻找左边最高的柱子,没有高于它的情况, lheight就是它本身的高度
            for (int j = i - 1; j >= 0; j--) {
                if (height[j] > lheight) {
                    lheight = height[j];
                }
            }
            // // 寻找右边最高的柱子,没有高于它的情况, rheight就是它本身的高度
            for (int j = i + 1; j <= height.length - 1; j++) {
                if (height[j] > rheight) {
                    rheight = height[j];
                }
            }
            res += Math.min(rheight, lheight) - height[i];
        }
        return res;
    }
}

解题思路主要就是一列一列的求此列可以积的雨水量,怎么求该列积的雨水量呢?也就是需要分别找到该列左右最高的列,然后用最小值去减去该列的值,当该列的左右没有比他更小的,那么该列的值就是最小值,最后求得的值也是0.
这道题使用双指针法的时间和空间复杂度还是挺高的,后面来补充动规和单调栈的方法。

你可能感兴趣的:(代码随想录,算法,leetcode,c++)