Leetcode热题100道

382 阅读16分钟

Leetcode热题100道

👏作者简介:大家好,我是 枫度柚子🍁,Java摆烂选手,很高兴认识大家 👀

📕CSDN/掘金/B站: 枫吹过的柚 🍁

🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦

🛰微信群: 加微信 QaQ-linv

🐧QQ群: 995832569

哈希

两数之和_1

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]
class Solution {
    public int[] twoSum(int[] nums, int target) {
        if (nums == null || nums.length == 0)
            return new int[] {};
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(target - nums[i])) {
                return new int[] { map.get(target - nums[i]), i };
            }
            map.put(nums[i], i);
        }
        return new int[] {};
    }
}

字母异位词分组_49

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:

输入: strs = [""]
输出: [[""]]

示例 3:

输入: strs = ["a"]
输出: [["a"]]
  • 将每串字符串的字符进行排序后作为key,然后放到对应的value集合里
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        if (strs == null || strs.length == 0)
            return new ArrayList<>();
        Map<String, List<String>> map = new HashMap<>();
        for (String str : strs) {
            char[] chs = str.toCharArray();
            Arrays.sort(chs);
            String key = new String(chs);
            // 根据key获取value集合
            List<String> values = map.getOrDefault(key, new ArrayList<>());
            values.add(str);
            map.put(key, values);
        }
        return new ArrayList<>(map.values());
    }
}

最长连续序列_128

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

双指针

滑动窗口

子串

普通数组

矩阵

链表

二叉树

图论

回溯

二分查找

  • int matrix = {{1, 3, 5, 7}, {10, 11, 16, 20}, {23, 30, 34, 60}}

    • int m = matrix.length; 行数
    • int n = matrix[0].length; 列数

搜索插入位置_35

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4
class Solution {
    public int searchInsert(int[] nums, int target) {
        if (nums == null || nums.length == 0)
            return -1;
        int l = 0, r = nums.length - 1;
        while (l <= r) { 
            int mid = l + (r - l) / 2;
          	if(target > nums[mid]) l = mid + 1;
            else r = mid - 1;
        }
        // 没找到就插入 程序退出条件应该是l==n了,所以返回l和n
        return l;
    }
}

搜索二维矩阵_74

给你一个满足下述两条属性的 m x n 整数矩阵:

  • 每行中的整数从左到右按非严格递增顺序排列。
  • 每行的第一个整数大于前一行的最后一个整数。

给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false

示例 1:

img

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true

示例 2:

img

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
      	// m为行数、n为列数
			  int m = matrix.length,n = matrix[0].length;
      	// 把二维数组映射到一维
        int l = 0,r = m * n - 1;
        while(l <= r){
          int mid = l + (r - l) / 2;
          if(target == get(matrix,mid)) return true;
          else if(target < get(matrix,mid)) r = mid - 1;
          else if(target > get(matrix,mid)) l = mid + 1;
        }
        return false;
    }
  
    // 通过一维坐标访问二维数组的坐标的元素
    int get(int[][] matrix,int index){
        int n = matrix[0].length;
        int i = index / n, j = index % n;
        return matrix[i][j];
    }
}

在排序数组中查找元素的第一个和最后一个位置_34

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]
  • 分为两个二分查找对应前、后下标,但是需要注意first前面的值可能等于mid 或 second后面的值等于mid
class Solution {
    public int[] searchRange(int[] nums, int target) {
        if (nums == null || nums.length == 0)
            return new int[] { -1, -1 };
        // first => 0 -> len - 1 ,没找到返回-1
        int first = findFirst(nums, target, 0, nums.length - 1);
        // second => first -> len - 1,没找到返回-1
        int second = findSecond(nums, target, Math.max(first, 0), nums.length - 1);
        return new int[] { first, second };
    }

    int findFirst(int[] nums, int target, int l, int r) {
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (target == nums[mid]) {
                // 说明mid之前就出现了
                if (mid - 1 >= 0 && nums[mid - 1] == nums[mid]) {
                    r = mid - 1;
                } else {
                    return mid;
                }
            } else if (target < nums[mid]) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        return -1;
    }

    int findSecond(int[] nums, int target, int l, int r) {
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (target == nums[mid]) {
                // 说明mid之前就出现了
                if (mid + 1 <= r && nums[mid + 1] == nums[mid]) {
                    l = mid + 1;
                } else {
                    return mid;
                }
            } else if (target < nums[mid]) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        return -1;
    }
}

搜索旋转排序数组_33

整数数组 nums 按升序排列,数组中的值 互不相同

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

示例 3:

输入:nums = [1], target = 0
输出:-1
  • 数组经过旋转会保证两块有序,分别进行处理
class Solution {
    public int search(int[] nums, int target) {
        if (nums == null || nums.length == 0)
            return -1;
        int l = 0, r = nums.length - 1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (target == nums[mid])
                return mid;
            // 如果左边有序
            else if (nums[mid] >= nums[l]) { // 注意
                if (target >= nums[l] && target < nums[mid]) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }
            // 如果右边有序
            else {
                if (target <= nums[r] && target > nums[mid]) {
                    l = mid + 1;
                } else {
                    r = mid - 1;
                }
            }
        }
        return -1;
    }
}

寻找旋转排序数组中的最小值_153

已知一个长度为 n 的数组,预先按照升序排列,经由 1n旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

示例 2:

输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 3 次得到输入数组。

示例 3:

输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。
  • 由于数组不包含重复元素,并且只要当前的区间长度不为 1,mid就不会与r重合
class Solution {
    public int findMin(int[] nums) {
        if (nums == null || nums.length == 0)
            return -1;
        int l = 0, r = nums.length - 1;
        while (l < r) { // 注意
            int mid = l + (r - l) / 2;
            if (nums[mid] < nums[r]) {
                r = mid;
            } else {
                l = mid + 1;
            }
        }
        return nums[l];
    }
}

寻找两个正序数组的中位数_4

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n))

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        // 用 len 表示合并后数组的长度,
        // 如果是奇数,我们需要知道第 (len+1)/2 个数就可以了,如果遍历的话需要遍历 int(len/2 ) + 1 次。
        // 如果是偶数,我们需要知道第 len/2和 len/2+1 个数
        // 也是需要遍历 len/2+1 次。所以遍历的话,奇数和偶数都是 len/2+1 次
        // 返回中位数的话,奇数需要最后一次遍历的结果就可以了,偶数需要最后一次和上一次遍历的结果
        // 所以我们用两个变量 leftrightright 保存当前循环的结果,在每次循环前将 right 的值赋给 left
        // 这样在最后一次循环的时候,left 将得到 right 的值,也就是上一次循环的结果,接下来 right 更新为最后一次的结果
        int m = nums1.length, n = nums2.length;
        int len = m + n;
        int left = -1, right = -1;
        // 用 num1_start 和 num2_start 分别表示当前指向 A 数组和 B 数组的位置
        int num1_start = 0, num2_start = 0;
        for (int i = 0; i < (len / 2) + 1; i++) {
            left = right;
            // ( 如果left < right 说明在left的数组里 或者越界了)
            if (num1_start < m && (num2_start >= n || nums1[num1_start] < nums2[num2_start])) {
                right = nums1[num1_start++];
            } else {
                // 如果left > right 说明在right的数组里
                right = nums2[num2_start++];
            }
        }
        // 如果是偶数
        if ((len & 1) == 0) {
            return (left + right) / 2.0;
        } else {
            return right;
        }
    }
}

  • Stack

    • push 入栈
    • pop 出栈并返回元素

有效的括号_20

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

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = "()"
输出:true

示例 2:

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

示例 3:

输入:s = "(]"
输出:false
  • 遍历每个字符,如果是左括号就入栈,否则就进行左出栈判断当前右括号与它是否匹配,不匹配就false,最终栈为空说明满足
class Solution {
    public boolean isValid(String s) {
        if (s == null || s.length() == 0)
            return false;
        Stack<Character> stack = new Stack<>();
        for (char c : s.toCharArray()) {
            if (c == '(' || c == '{' || c == '[') {
                stack.push(c);
            } else {
                if (!stack.isEmpty()) {
                    Character top = stack.pop();
                    if (!(  (c == ')' && top == '(') ||
                            (c == ']' && top == '[') ||
                            (c == '}' && top == '{'))) {
                        return false;
                    }
                } else {
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }

}

最小栈_155

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

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。

示例 1:

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.
  • 通过两个Stack完成

    • 一个Stack正常存
    • 一个minStack先判断是否为空,为空就先存,不为空时取当前值和栈顶元素小的那个存
class MinStack {

    Stack<Integer> stack;
    Stack<Integer> minStack;

    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }

    public void push(int val) {
        stack.push(val);
        // 如果为空就存当前值
        if (minStack.isEmpty()) {
            minStack.push(val);
        } else {
            minStack.push(Math.min(minStack.peek(), val));
        }

    }

    public void pop() {
        if (stack.isEmpty())
            return;
        stack.pop();
        minStack.pop();
    }

    public int top() {
        return stack.peek();
    }

    public int getMin() {
        return minStack.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

字符串解码_394

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a2[4] 的输入。

示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"

示例 2:

输入:s = "3[a2[c]]"
输出:"accaccacc"

示例 3:

输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"

示例 4:

输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"
  • 一个Stack存倍数 numStack
  • 一个Stack存上次结果 resStack
  • 每次出栈用来追加后面的字符,需要注意的是数字可能不是个位数,当为左括号时,先预留一串空的,当为右括号时就要开始乘法得到一串字符串segment,resStack栈顶出栈 + segment 即为当前res
class Solution {
    public String decodeString(String s) {
        if (s == null || s.length() == 0)
            return "";
        Stack<Integer> numStack = new Stack<>();
        Stack<String> resStack = new Stack<>();
        int num = 0;
        StringBuilder res = new StringBuilder();
        for (char c : s.toCharArray()) {
            if (c >= '0' && c <= '9') {
                num = num * 10 + c - '0';
            } else if (c == '[') {
                numStack.push(num);
                resStack.push(res.toString());
                num = 0;
                res = new StringBuilder();
            } else if (c == ']') {
                StringBuilder segment = new StringBuilder();
                Integer curNum = numStack.pop();
                for (int i = 0; i < curNum; i++) {
                    segment.append(res.toString());
                }
                // 上次的出栈 + 本段字符
                res = new StringBuilder(resStack.pop() + segment);
            } else {
                // 将括号中的字符拼出来
                res.append(c);
            }
        }
        return res.toString();
    }
}

每日温度_739

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例 1:

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]

示例 2:

输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]

示例 3:

输入: temperatures = [30,60,90]
输出: [1,1,0]
  • 用Stack(存比当前值小的下标)

    • 遍历数组,判断当前值是否比Stack栈顶元素对应的值小,如果小就Stack栈顶元素出栈,存当前下标
  • 最终结果res通过 当前值下标 - 栈顶元素的值(下标)

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        if (temperatures == null || temperatures.length == 0)
            return new int[] {};
        Stack<Integer> stack = new Stack<>();
        int[] res = new int[temperatures.length];
        for (int i = 0; i < temperatures.length; i++) {
           // 保证出栈非空,有大的就出栈一个,没有默认都为0
            while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
                Integer curTopIdx = minStack.pop();
                res[curTopIdx] = i - curTopIdx;
            }
            stack.push(i);
        }
        return res;
    }
}

柱状图中最大的矩形_84

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例 1:

img

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

img

输入: heights = [2,4]
输出: 4
  • 用Stack(存入比当前值大的下标),计算面积 = width(右边下标 - 左边下标 - 1) * 较低高度(下标对应的值)
class Solution {
    public int largestRectangleArea(int[] heights) {
        if (heights == null || heights.length == 0)
            return 0;
        Stack<Integer> stack = new Stack<>();
        int max = 0;
        // 将元素放入新数组居中
        int[] newHeights = new int[heights.length + 2];
        newHeights[0] = 0;
        newHeights[newHeights.length - 1] = 0;
        for (int i = 1; i < heights.length + 1; i++) {
            newHeights[i] = heights[i - 1];
        }
​
        for (int i = 0; i < newHeights.length; i++) {
            // 栈顶元素对应的值 > 当前元素的值
            while (!stack.isEmpty() && newHeights[i] < newHeights[stack.peek()]) {
                // 栈顶元素,之前最小的高度
                int height = newHeights[stack.pop()];
                int left = stack.peek();
                int width = i - left - 1;
                int area = width * height;
                max = Math.max(max, area);
            }
            stack.push(i);
        }
​
        return max;
​
    }
}

  • PriorityQueue

    • offer 新增
    • poll 删除头元素并返回
    • peek 获取头

数组中的第K个最大元素_215

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入: [3,2,1,5,6,4], k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4
  • 采用最小堆,元素入堆,剔除前K大的元素
class Solution {
    public int findKthLargest(int[] nums, int k) {
        if(nums == null || nums.length == 0) return 0;
        // 使用最小堆
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        for (int num : nums) {
            minHeap.offer(num);
            if (minHeap.size() > k) {
                minHeap.poll();
            }
        }
        return minHeap.peek();
    }
}

前K个高频元素_347

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

示例 1:

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

示例 2:

输入: nums = [1], k = 1
输出: [1]
  • 采用Map存放出现频率,再使用最小堆,遍历频率Map的key,剔除前K大的元素
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        if (nums == null || nums.length == 0)
            return new int[] {};
        Map<Integer, Integer> freqMap = new HashMap<>();
        for (int num : nums) {
            freqMap.put(num, freqMap.getOrDefault(num, 0) + 1);
        }
        // 使用最小堆
        PriorityQueue<Integer> minHeap = new PriorityQueue<>((a, b) -> freqMap.get(a) - freqMap.get(b));
        // 遍历频率Map
        for (Integer num : freqMap.keySet()) {
            minHeap.offer(num);
            if (minHeap.size() > k) {
                minHeap.poll();
            }
        }
        int[] res = new int[k];
        for (int i = 0; i < k; i++) {
            res[i] = minHeap.poll();
        }
        return res;
    }
}

数据流的中位数_295

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

  • 例如 arr = [2,3,4] 的中位数是 3
  • 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5

实现 MedianFinder 类:

  • MedianFinder()初始化 MedianFinder 对象。
  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中。
  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]

解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0
  • 借助两个堆,长度不相等

    • 最小堆放元素,然后放最小堆的poll
    • 最大堆放元素,然后放最大堆的poll
class MedianFinder {

    PriorityQueue<Integer> small;
    PriorityQueue<Integer> large;

    public MedianFinder() {
        // 最小堆 保存较大的一半
        small = new PriorityQueue<>();
        // 最大堆 保存较小的一半
        large = new PriorityQueue<>((a, b) -> b - a);
    }

    public void addNum(int num) {
        if (small.size() != large.size()) {
            small.offer(num);
            // 存小的
            large.offer(small.poll());
        } else {
            large.offer(num);
            // 存大的
            small.offer(large.poll());
        }
    }

    public double findMedian() {
        if (small.size() > large.size()) {
            return small.peek();
        } else if (small.size() < large.size()) {
            return large.peek();
        } else {
            return (small.peek() + large.peek()) / 2.0;
        }

    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */

贪心算法

动态规划

多维动态规划

技巧