今天看到这道题目:http://www.cnblogs.com/charlesblc/p/5930311.html
题目地址:https://leetcode.com/problems/split-array-largest-sum/
很好,也很难。开拓了思路,用二分法来查找结果备选,然后直接划分原集合,来反过来是否是合理的解。是或者不是的话,继续向相应的方向进行二分。
| # | Title | Editorial | Acceptance | Difficulty | Frequency | |
|---|---|---|---|---|---|---|
| 。 | 454 | 43.3% | Medium | |||
| 。 | 437 | 38.7% | Easy | |||
| 。 | 416 | 37.5% | Medium | |||
| 。 | 410 | 32.2% | Hard | |||
| 。 | 404 | 45.7% | Easy | |||
| 。 | 377 | 41.6% | Medium | |||
| 。 | 373 | 29.8% | Medium | |||
| 。 | 371 | 51.4% | Easy | |||
| 。 | 364 | 50.4% | Medium | |||
| 。 | 363 | 32.1% | Hard | |||
| 。 | 339 | 59.8% | Easy | |||
| 。 | 327 | 28.9% | Hard | |||
| 。 | 325 | 41.6% | Medium | |||
| 。 | 308 | 19.8% | Hard | |||
| 。 | 307 | 18.4% | Medium | |||
| 。 | 304 | 22.7% | Medium | |||
| 。 | 303 | 26.2% | Easy | |||
| 。 | 259 | 40.6% | Medium | |||
| 。 | 228 | 28.1% | Medium | |||
| 。 | 216 | 41.9% | Medium | |||
| 。 | 209 | 28.6% | Medium | |||
| 。 | 170 | 22.7% | Easy | |||
| 。 | 167 | 48.2% | Medium | |||
| 。 | 129 | 35.2% | Medium | |||
| 。 | 124 | 25.0% | Hard | |||
| 。 | 113 | 31.4% | Medium | |||
| 。 | 112 | 32.9% | Easy | |||
| 。 | 64 | 37.2% | Medium | |||
| 。 | 40 | 31.3% | Medium | |||
| 。 | 39 | 35.7% | Medium | |||
| 。 | 18 | 25.7% | Medium | |||
| 。 | 16 | 30.5% | Medium | |||
| 。 | 15 | 20.9% | Medium | |||
| 。 | 1 | 29.9% | Easy |
先看的
| 363 | Max Sum of Rectangle No Larger Than K | 32.1% | Hard |
很难。看解法的时候,还看到一个很好的解法分析的页面:Link,能够处理 连续子数组求最大和 问题。
这个页面很好,层层递进,从最原始的O(n^3)的暴力查找,到记录sum(i)到i为止的和,然后sum(j)-sum(i) 得到sum(i...j)的方式,提升到O(n^2);然后,通过分治法,一分为二的左右查找,然后中间从两边扩展再求和,将复杂度进一步有划到O(nlgn)。最后,解决DP的思想,直接采用max(j)= max(i) + sum(i...j)的方式,但是优化的是,max(i)如果是负数,就不用记录,用0就行了,所以其实可以O(n)的复杂度,直接进行求和,遇到和为负数就舍弃就可以了,复杂度为O(n),已经最优化。(Kadane算法)
注意一个引申的是,找出和最大的矩阵。
我的方法是,先纵向处理,用sum(i)记录同一列中从最上方到i行的和,然后用sum(j)-sum(i)就能够求出这一列中的sum(i...j),复杂度O(n),
然后对于横向,对于每一个行的组合,用sum(i...j)作为基本元素,采用上面的Kadane算法来处理,复杂度一共 O(n*n*m),所以取较小的一个维度为n.
而上面原题是对于sum有限制的,不大于某个数(比如k)的最大和。
参考解法里面的带的文章,可以得出,求出sum(i)后放入一个set,每次对于sum(j),用一个upper_bound获得不小于sum(j)-k的sum(i),这样sum(j)-sum(i)就小于k,并且是当前最大的了。
upper_bound是取得右侧范围,复杂度是O(lgn),所以对于一维数组,复杂度是 O(nlgn),而对于矩阵,复杂度是:
O[min(m,n)^2 * max(m,n) * log(max(m,n))], space O(max(m, n)).
特别注意的是,对于上面的space O(max(m, n)) 指的是对于用一列的结果(假设矩阵是扁的)不需要保存每一个sum(j), sum(i),只需要循环的时候,直接得出同一列里面sum(i...j)就可以了,而对于横向,行的结果(每一行都很长),是需要算出每一个sum(a), 并且把结果保存到set里面的。
另外注意,stl里面set的底层实现是红黑树。另,map的底层实现也是红黑树。vector是动态数组实现。
另,用Java的TreeSet的ceiling可以模拟upper_bound(获得更大范围里面最小的数),floor可以模拟 lower_bound(获得更小范围内最大的数)。
好了,看这道题目:
| 327 | Count of Range Sum | 28.9% | Hard |
我觉得是不是可以参考之前的放在set里面,然后用upper_bound或者lower_bound来处理。但是要注意的是,要用multiset,因为可能会有重复的元素。
解法中,也提到,可以用divide-and-conque方法,复杂度比naive方法,有所优化。
另外解法里提到的 https://discuss.leetcode.com/topic/33738/share-my-solution 里面关于 MergeSort模拟的方法,的确是正确的,虽然稍稍有点费解。仔细看了一下,还是能够理解的。
利用MergeSort还能处理个子排序的问题,就是一个人后面有几个人比自己高吧。有很多变通。
这道题目:
| 308 | Range Sum Query 2D - Mutable | 19.8% | Hard |
在locked文件里面,有解法。
注意题目中有一句 "calls to update and sumRegion function is distributed evenly"
在update和sumRegion调用频率不同的情况下,可以有不同的对策。而对于调用频率相似的情况,那么解法中也给了折衷的方案。
当然,还有线段树的方法,但是面试中,一般不会涉及这一块。
| 124 | Binary Tree Maximum Path Sum | 25.0% | Hard |
解法里面有。基本就是两课子树的递归,然后分别给出带root和不带root的,然后再用带root的跟本身的root做一下结合和比较处理。
| 18 | 4Sum | 25.7% | Medium |
解法非常好。我也想到了一定的思路。基本的基础是排序后两边向中间收敛。但是4sum多了对更多的两个元素的处理,以及一些去重的处理。
很好!
| 16 | 3Sum Closest | 30.5% | Medium |
开始我想到的是,先两端向中间收敛,然后找备选的第三个,但是发现实现有问题。所以更好的方法是,先选定一个,然后两端向中间收敛,通过与目标的比较,来确定是向上还是向下收敛。这种解法更好!
| 15 | 3Sum | 20.9% | Medium |
标准的固定一个,两边向中间收敛的解法。
| 39 | Combination Sum | 35.7% | Medium |
标准的递归解法。先取一个元素,然后多次迭加之后,看剩余的数字,是否还有其他的结果。可以用DP.
| 40 | Combination Sum II | 31.3% | Medium |
跟上面那个解法类似,就是每次去掉一个元素,同时这样也是可以用DP的。
| 216 | Combination Sum III | 41.9% | Medium |
比上面那个简单一些,也是遍历。
| 377 | Combination Sum IV | 41.6% | Medium |
这个跟Combination Sum 1 很相似,但是这个对于不同排序的结果算作多个。所以每次不用从下一个位置开始遍历,直接所有待选元素,是不是target减数字就可以了。
| 64 | Minimum Path Sum | 37.2% |
Medium |
这道题目,可以看每一个点,到这个位置的最小代价。
| 1 | Two Sum | 29.9% | Easy |
基本题。
| 112 | Path Sum | 32.9% | Easy |
基本题,每次去掉root,然后递归。
| 113 | Path Sum II | 31.4% | Medium |
跟上面那道题目一样的。
| 437 | Path Sum III | 38.7% | Easy |
其实就是用了个递归做的。还是应该多考虑DP的。
| 454 | 4Sum II | 43.3% | Medium |
好!开始我想到的是先固定两个,然后另外两个,从两端向中间收敛。但是会有不少重复的情况。而我实际做的是,两两相加,通过Map或者HashMap来进行匹配,这样的话,其实是更加快的,而且应该重复的情况不多。
| 129 | Sum Root to Leaf Numbers | 35.2% | Medium |
用的DFS深度遍历,用一个curVal记录当前的值。
| 209 | Minimum Size Subarray Sum | 28.6% | Medium |
题目还是蛮好的。设置两个游标,一前一后。
| 259 | 3Sum Smaller | 40.6% | Medium |
下面代码是最终的解法,注意其中有两点:1是ret+的是end和front的距离,2是用了java的容器来实现引用传递。
public class Solution { public int threeSumSmaller(int[] nums, int target) { if (nums == null) throw new IllegalArgumentException("nums is null"); List<Integer> ret = new ArrayList<Integer> (); ret.add(0); Arrays.sort(nums); for (int i = 0; i < nums.length - 2; i++) { twoSumSmaller(nums, i + 1, target - nums[i], ret); } return ret.get(0); } private void twoSumSmaller(int[] nums, int i, int target, List<Integer> ret) { int front = i; int end = nums.length - 1; while (front < end) { if (nums[front] + nums[end] < target) { ret.set(0, ret.get(0) + end - front); front++; } else{ end--; } } } }