diff --git a/notes/Leetcode 题解.md b/notes/Leetcode 题解.md index 58531a3b..cf56be82 100644 --- a/notes/Leetcode 题解.md +++ b/notes/Leetcode 题解.md @@ -74,7 +74,7 @@ You need to output 2. 题目描述:每个孩子都有一个满足度,每个饼干都有一个大小,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。 -因为最小的孩子最容易得到满足,因此先满足最小孩子。给一个孩子的饼干应当尽量小又能满足该孩子,这样大饼干就能拿来给满足度比较大的孩子。 +因为最小的孩子最容易得到满足,因此先满足最小孩子。给一个孩子的饼干应当尽量小又能满足该孩子,这样大饼干就能拿来给满足度比较大的孩子。因此贪心策略 证明:假设在某次选择中,贪心策略选择给当前满足度最小的孩子分配第 m 个饼干,第 m 个饼干为可以满足该孩子的最小饼干。假设存在一种最优策略,给该孩子分配第 n 个饼干,并且 m < n。我们可以发现,经过这一轮分配,贪心策略分配后剩下的饼干一定有一个比最优策略来得大。因此在后续的分配中,贪心策略一定能满足更多的孩子。也就是说不存在比贪心策略更优的策略,即贪心策略就是最优策略。 @@ -93,105 +93,6 @@ public int findContentChildren(int[] g, int[] s) { } ``` -**股票的最大收益** - -[122. Best Time to Buy and Sell Stock II (Easy)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/description/) - -题目描述:一次交易包含买入和卖出,多个交易之间不能交叉进行。 - -对于 [a, b, c, d],如果有 a <= b <= c <= d ,那么最大收益为 d - a。而 d - a = (d - c) + (c - b) + (b - a) ,因此当访问到一个 prices[i] 且 prices[i] - prices[i-1] > 0,那么就把 prices[i] - prices[i-1] 添加到收益中,从而在局部最优的情况下也保证全局最优。 - -```java -public int maxProfit(int[] prices) { - int profit = 0; - for (int i = 1; i < prices.length; i++) - if (prices[i] > prices[i - 1]) - profit += (prices[i] - prices[i - 1]); - - return profit; -} -``` - -**种植花朵** - -[605. Can Place Flowers (Easy)](https://leetcode.com/problems/can-place-flowers/description/) - -```html -Input: flowerbed = [1,0,0,0,1], n = 1 -Output: True -``` - -题目描述:花朵之间至少需要一个单位的间隔,求解是否能种下 n 朵花。 - -```java -public boolean canPlaceFlowers(int[] flowerbed, int n) { - int len = flowerbed.length; - int cnt = 0; - for (int i = 0; i < len; i++) { - if (flowerbed[i] == 1) - continue; - int pre = i == 0 ? 0 : flowerbed[i - 1]; - int next = i == len - 1 ? 0 : flowerbed[i + 1]; - if (pre == 0 && next == 0) { - cnt++; - flowerbed[i] = 1; - } - } - return cnt >= n; -} -``` - -**修改一个数成为非递减数组** - -[665. Non-decreasing Array (Easy)](https://leetcode.com/problems/non-decreasing-array/description/) - -```html -Input: [4,2,3] -Output: True -Explanation: You could modify the first 4 to 1 to get a non-decreasing array. -``` - -题目描述:判断一个数组能不能只修改一个数就成为非递减数组。 - -在出现 nums[i] < nums[i - 1] 时,需要考虑的是应该修改数组的哪个数,使得本次修改能使 i 之前的数组成为非递减数组,并且 **不影响后续的操作** 。优先考虑令 nums[i - 1] = nums[i],因为如果修改 nums[i] = nums[i - 1] 的话,那么 nums[i] 这个数会变大,就有可能比 nums[i + 1] 大,从而影响了后续操作。还有一个比较特别的情况就是 nums[i] < nums[i - 2],只修改 nums[i - 1] = nums[i] 不能使数组成为非递减数组,只能修改 nums[i] = nums[i - 1]。 - -```java -public boolean checkPossibility(int[] nums) { - int cnt = 0; - for (int i = 1; i < nums.length && cnt < 2; i++) { - if (nums[i] >= nums[i - 1]) - continue; - cnt++; - if (i - 2 >= 0 && nums[i - 2] > nums[i]) - nums[i] = nums[i - 1]; - else - nums[i - 1] = nums[i]; - } - return cnt <= 1; -} -``` - -**判断是否为子串** - -[392. Is Subsequence (Medium)](https://leetcode.com/problems/is-subsequence/description/) - -```html -s = "abc", t = "ahbgdc" -Return true. -``` - -```java -public boolean isSubsequence(String s, String t) { - int index = -1; - for (char c : s.toCharArray()) { - index = t.indexOf(c, index + 1); - if (index == -1) - return false; - } - return true; -} -``` - **不重叠的区间个数** [435. Non-overlapping Intervals (Medium)](https://leetcode.com/problems/non-overlapping-intervals/description/) @@ -214,7 +115,7 @@ Explanation: You don't need to remove any of the intervals since they're already 题目描述:计算让一组区间不重叠所需要移除的区间个数。 -直接计算最多能组成的不重叠区间个数即可。 +计算最多能组成的不重叠区间个数,然后用区间总个数减去不重叠区间的个数。 在每次选择中,区间的结尾最为重要,选择的区间结尾越小,留给后面的区间的空间越大,那么后面能够选择的区间个数也就越大。 @@ -222,14 +123,16 @@ Explanation: You don't need to remove any of the intervals since they're already ```java public int eraseOverlapIntervals(Interval[] intervals) { - if (intervals.length == 0) + if (intervals.length == 0) { return 0; + } Arrays.sort(intervals, Comparator.comparingInt(o -> o.end)); int cnt = 1; int end = intervals[0].end; for (int i = 1; i < intervals.length; i++) { - if (intervals[i].start < end) + if (intervals[i].start < end) { continue; + } end = intervals[i].end; cnt++; } @@ -237,6 +140,17 @@ public int eraseOverlapIntervals(Interval[] intervals) { } ``` +使用 lambda 表示式创建 Comparator 会导致算法运行时间过长,如果注重运行时间,可以修改为普通创建 Comparator 语句: + +```java +Arrays.sort(intervals, new Comparator() { + @Override + public int compare(Interval o1, Interval o2) { + return o1.end - o2.end; + } +}); +``` + **投飞镖刺破气球** [452. Minimum Number of Arrows to Burst Balloons (Medium)](https://leetcode.com/problems/minimum-number-of-arrows-to-burst-balloons/description/) @@ -255,15 +169,15 @@ Output: ```java public int findMinArrowShots(int[][] points) { - if (points.length == 0) + if (points.length == 0) { return 0; - + } Arrays.sort(points, Comparator.comparingInt(o -> o[1])); - int cnt = 1, end = points[0][1]; for (int i = 1; i < points.length; i++) { - if (points[i][0] <= end) // [1,2] 和 [2,3] 算重叠 + if (points[i][0] <= end) { continue; + } cnt++; end = points[i][1]; } @@ -271,42 +185,6 @@ public int findMinArrowShots(int[][] points) { } ``` - -**分隔字符串使同种字符出现在一起** - -[763. Partition Labels (Medium)](https://leetcode.com/problems/partition-labels/description/) - -```html -Input: S = "ababcbacadefegdehijhklij" -Output: [9,7,8] -Explanation: -The partition is "ababcbaca", "defegde", "hijhklij". -This is a partition so that each letter appears in at most one part. -A partition like "ababcbacadefegde", "hijhklij" is incorrect, because it splits S into less parts. -``` - -```java - public List partitionLabels(String S) { - int[] lastIndexs = new int[26]; - for (int i = 0; i < S.length(); i++) - lastIndexs[S.charAt(i) - 'a'] = i; - - List ret = new ArrayList<>(); - int firstIndex = 0; - while (firstIndex < S.length()) { - int lastIndex = firstIndex; - for (int i = firstIndex; i < S.length() && i <= lastIndex; i++) { - int index = lastIndexs[S.charAt(i) - 'a']; - if (index > lastIndex) - lastIndex = index; - } - ret.add(lastIndex - firstIndex + 1); - firstIndex = lastIndex + 1; - } - return ret; - } -``` - **根据身高和序号重组队列** [406. Queue Reconstruction by Height(Medium)](https://leetcode.com/problems/queue-reconstruction-by-height/description/) @@ -327,20 +205,163 @@ Output: ```java public int[][] reconstructQueue(int[][] people) { - if (people == null || people.length == 0 || people[0].length == 0) + if (people == null || people.length == 0 || people[0].length == 0) { return new int[0][0]; - + } Arrays.sort(people, (a, b) -> (a[0] == b[0] ? a[1] - b[1] : b[0] - a[0])); - List queue = new ArrayList<>(); - - for (int[] p : people) + for (int[] p : people) { queue.add(p[1], p); - + } return queue.toArray(new int[queue.size()][]); } ``` +**分隔字符串使同种字符出现在一起** + +[763. Partition Labels (Medium)](https://leetcode.com/problems/partition-labels/description/) + +```html +Input: S = "ababcbacadefegdehijhklij" +Output: [9,7,8] +Explanation: +The partition is "ababcbaca", "defegde", "hijhklij". +This is a partition so that each letter appears in at most one part. +A partition like "ababcbacadefegde", "hijhklij" is incorrect, because it splits S into less parts. +``` + +```java +public List partitionLabels(String S) { + int[] lastIndexsOfChar = new int[26]; + for (int i = 0; i < S.length(); i++) { + lastIndexsOfChar[char2Index(S.charAt(i))] = i; + } + List partitions = new ArrayList<>(); + int firstIndex = 0; + while (firstIndex < S.length()) { + int lastIndex = firstIndex; + for (int i = firstIndex; i < S.length() && i <= lastIndex; i++) { + int index = lastIndexsOfChar[char2Index(S.charAt(i))]; + if (index > lastIndex) { + lastIndex = index; + } + } + partitions.add(lastIndex - firstIndex + 1); + firstIndex = lastIndex + 1; + } + return partitions; +} + +private int char2Index(char c) { + return c - 'a'; +} +``` + + +**种植花朵** + +[605. Can Place Flowers (Easy)](https://leetcode.com/problems/can-place-flowers/description/) + +```html +Input: flowerbed = [1,0,0,0,1], n = 1 +Output: True +``` + +题目描述:花朵之间至少需要一个单位的间隔,求解是否能种下 n 朵花。 + +```java +public boolean canPlaceFlowers(int[] flowerbed, int n) { + int len = flowerbed.length; + int cnt = 0; + for (int i = 0; i < len && cnt < n; i++) { + if (flowerbed[i] == 1) { + continue; + } + int pre = i == 0 ? 0 : flowerbed[i - 1]; + int next = i == len - 1 ? 0 : flowerbed[i + 1]; + if (pre == 0 && next == 0) { + cnt++; + flowerbed[i] = 1; + } + } + return cnt >= n; +} +``` + +**判断是否为子串** + +[392. Is Subsequence (Medium)](https://leetcode.com/problems/is-subsequence/description/) + +```html +s = "abc", t = "ahbgdc" +Return true. +``` + +```java +public boolean isSubsequence(String s, String t) { + int index = -1; + for (char c : s.toCharArray()) { + index = t.indexOf(c, index + 1); + if (index == -1) { + return false; + } + } + return true; +} +``` + +**修改一个数成为非递减数组** + +[665. Non-decreasing Array (Easy)](https://leetcode.com/problems/non-decreasing-array/description/) + +```html +Input: [4,2,3] +Output: True +Explanation: You could modify the first 4 to 1 to get a non-decreasing array. +``` + +题目描述:判断一个数组能不能只修改一个数就成为非递减数组。 + +在出现 nums[i] < nums[i - 1] 时,需要考虑的是应该修改数组的哪个数,使得本次修改能使 i 之前的数组成为非递减数组,并且 **不影响后续的操作** 。优先考虑令 nums[i - 1] = nums[i],因为如果修改 nums[i] = nums[i - 1] 的话,那么 nums[i] 这个数会变大,就有可能比 nums[i + 1] 大,从而影响了后续操作。还有一个比较特别的情况就是 nums[i] < nums[i - 2],只修改 nums[i - 1] = nums[i] 不能使数组成为非递减数组,只能修改 nums[i] = nums[i - 1]。 + +```java +public boolean checkPossibility(int[] nums) { + int cnt = 0; + for (int i = 1; i < nums.length && cnt < 2; i++) { + if (nums[i] >= nums[i - 1]) { + continue; + } + cnt++; + if (i - 2 >= 0 && nums[i - 2] > nums[i]) { + nums[i] = nums[i - 1]; + } else { + nums[i - 1] = nums[i]; + } + } + return cnt <= 1; +} +``` + +**股票的最大收益** + +[122. Best Time to Buy and Sell Stock II (Easy)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/description/) + +题目描述:一次股票交易包含买入和卖出,多个交易之间不能交叉进行。 + +对于 [a, b, c, d],如果有 a <= b <= c <= d ,那么最大收益为 d - a。而 d - a = (d - c) + (c - b) + (b - a) ,因此当访问到一个 prices[i] 且 prices[i] - prices[i-1] > 0,那么就把 prices[i] - prices[i-1] 添加到收益中,从而在局部最优的情况下也保证全局最优。 + +```java +public int maxProfit(int[] prices) { + int profit = 0; + for (int i = 1; i < prices.length; i++) { + if (prices[i] > prices[i - 1]) { + profit += (prices[i] - prices[i - 1]); + } + } + return profit; +} +``` + ## 双指针 双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。