1.原题:https://leetcode.com/problems/median-of-two-sorted-arrays/description/
2.解释:
1 2 3 4 5 6 7 8 9
对于一个长度为n的已排序数列a,若n为奇数,中位数为a[n / 2 + 1] , 若n为偶数,则中位数(a[n / 2] + a[n / 2 + 1]) / 2 如果我们可以在两个数列中求出第K小的元素,便可以解决该问题 不妨设数列A元素个数为n,数列B元素个数为m,各自升序排序,求第k小元素 取A[k / 2] B[k / 2] 比较, 如果 A[k / 2] > B[k / 2] 那么,所求的元素必然不在B的前k / 2个元素中(证明反证法) 反之,必然不在A的前k / 2个元素中,于是我们可以将A或B数列的前k / 2元素删去,求剩下两个数列的 k - k / 2小元素,于是得到了数据规模变小的同类问题,递归解决 如果 k / 2 大于某数列个数,所求元素必然不在另一数列的前k / 2个元素中,同上操作就好。
3.代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
class {public : double findMedianSortedArrays (vector <int >& nums1, vector <int >& nums2) { int len1 = nums1.size(); int len2 = nums2.size(); int len = len1 + len2; if (len & 1 ) { return findkth(nums1, 0 , nums2, 0 , len / 2 + 1 ); } else { return (findkth(nums1, 0 , nums2, 0 , len / 2 ) + findkth(nums1, 0 , nums2, 0 , len / 2 + 1 )) / 2 ; } } double findkth (vector <int >& nums1, int index1, vector <int >& nums2, int index2, int k) { int len1 = nums1.size(); int len2 = nums2.size(); if (index1 >= len1) { return nums2[index2 + k - 1 ]; } if (index2 >= len2) { return nums1[index1 + k - 1 ]; } if (k == 1 ) { return min(nums1[index1], nums2[index2]); } int key1 = index1 + k / 2 - 1 >= len1 ? INT_MAX : nums1[index1 + k / 2 - 1 ]; int key2 = index2 + k / 2 - 1 >= len2 ? INT_MAX : nums2[index2 + k / 2 - 1 ]; if (key1 < key2) { return findkth(nums1, index1 + k / 2 , nums2, index2, k - k / 2 ); } else { return findkth(nums1, index1, nums2, index2 + k / 2 , k - k / 2 ); } } };
参考链接:http://blog.csdn.net/gao1440156051/article/details/51725845
30. Substring with Concatenation of All Words
1.原题:https://leetcode.com/problems/substring-with-concatenation-of-all-words/description/
2.解释:
1
NB解法:O(n)时间复杂度,设计思路非常巧妙。这种方法不再是一个字符一个字符的遍历,而是一个词一个词的遍历,比如根据题目中的例子,字符串s的长度n为18,words数组中有两个单词(cnt=2),每个单词的长度len均为3,那么遍历的顺序为0,3,6,8,12,15,然后偏移一个字符1,4,7,9,13,16,然后再偏移一个字符2,5,8,10,14,17,这样就可以把所有情况都遍历到,我们还是先用一个哈希表m1来记录words里的所有词,然后我们从0开始遍历,用left来记录左边界的位置,count表示当前已经匹配的单词的个数。然后我们一个单词一个单词的遍历,如果当前遍历的到的单词t在m1中存在,那么我们将其加入另一个哈希表m2中,如果在m2中个数小于等于m1中的个数,那么我们count自增1,如果大于了,那么需要做一些处理,比如下面这种情况, s = barfoofoo, words = {bar, foo, abc}, 我们给words中新加了一个abc,目的是为了遍历到barfoo不会停止,那么当遍历到第二foo的时候, m2[foo]=2, 而此时m1[foo]=1,这是后已经不连续了,所以我们要移动左边界left的位置,我们先把第一个词t1=bar取出来,然后将m2[t1]自减1,如果此时m2[t1]<m1[t1]了,说明一个匹配没了,那么对应的count也要自减1,然后左边界加上个len,这样就可以了。如果某个时刻count和cnt相等了,说明我们成功匹配了一个位置,那么将当前左边界left存入结果res中,此时去掉最左边的一个词,同时count自减1,左边界右移len,继续匹配。如果我们匹配到一个不在m1中的词,那么说明跟前面已经断开了,我们重置m2,count为0,左边界left移到j+len。
3.代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
class {public : vector <int > findSubstring(string s, vector <string >& words) { vector <int > res; int slen = s.length(); int wlen = words.size(); if (slen <= 0 || wlen <= 0 ) return res; int word_len = words[0 ].length(); if (word_len == 0 ) { for (int i = 0 ; i <= slen; i++) res.push_back(i); return res; } unordered_map <string , int > dict; for (int i = 0 ; i < wlen; i++) dict[words[i]]++; for (int i = 0 ; i < word_len; i++) { unordered_map <string , int > move_dict; int left = i, count = 0 ; for (int j = i; j <= slen - word_len; j += word_len) { string str = s.substr(j, word_len); if (dict.count(str)) { move_dict[str]++; if (move_dict[str] <= dict[str]) count++; else { while (move_dict[str] > dict[str]) { string temp = s.substr(left, word_len); move_dict[temp]--; if (move_dict[temp] < dict[temp]) count--; left += word_len; } } if (count == wlen) { res.push_back(left); move_dict[s.substr(left, word_len)]--; count--; left += word_len; } } else { move_dict.clear(); count = 0 ; left = j + word_len; } } } return res; } };
参考链接:
https://www.cnblogs.com/grandyang/p/4521224.html
https://leetcode.com/problems/substring-with-concatenation-of-all-words/discuss/13656/An-O(N)-solution-with-detailed-explanation
#回溯问题
1.相关问题:子集,组合和,排列,回文分区(Subsets, Permutations, Combination Sum, Palindrome Partitioning)等
2.子集Subsets(https://leetcode.com/problems/subsets/):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
public List<List<Integer>> subsets(int [] nums) { List<List<Integer>> list = new ArrayList<>(); Arrays.sort(nums); backtrack(list, new ArrayList<>(), nums, 0 ); return list; } private void backtrack (List<List<Integer>> list , List<Integer> tempList, int [] nums, int start) { list.add(new ArrayList<>(tempList)); for (int i = start; i < nums.length; i++){ tempList.add(nums[i]); backtrack(list, tempList, nums, i + 1 ); tempList.remove(tempList.size() - 1 ); } }
3.子集II(包含重复项)(https://leetcode.com/problems/subsets-ii/):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
class {public : vector<vector<int >> subsetsWithDup(vector<int >& nums) { vector<vector<int > > list; vector<bool> used (nums.size() , false ) ; sort(nums.begin(), nums.end()); backtrack(list, vector<int >(), nums, used, 0 ); return list; } void backtrack (vector<vector<int >>& list , vector<int > tempList, vector<int >& nums, vector<bool> used, int start) { list.push_back(tempList); for (int i = start; i < nums.size(); i++){ if (i > start && used[i - 1 ] == false && nums[i] == nums[i - 1 ]) continue ; tempList.push_back(nums[i]); used[i] = true ; backtrack(list, tempList, nums, used, i + 1 ); tempList.pop_back(); used[i] = false ; } } };
4.组合总和(https://leetcode.com/problems/combination-sum/):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
class {public : vector <vector <int >> combinationSum2(vector <int >& candidates, int target) { vector <vector <int >> res; if (candidates.size() == 0 ) return res; sort(candidates.begin(), candidates.end()); if (target < candidates[0 ]) return res; vector <int > combination; backTrack(res, candidates, combination, target, 0 ); return res; } private : void backTrack (vector <vector <int >>& res, vector <int > candidates, vector <int > combination, int target, int begin) { if (target < 0 ) return ; else if (target == 0 ) { res.push_back(combination); return ; } for (int i = begin; i < candidates.size() && target >= candidates[i]; i++) { combination.push_back(candidates[i]); backTrack(res, candidates, combination, target - candidates[i], i); combination.pop_back(); } } };
5.组合总和II(不能重复使用相同的元素)(https://leetcode.com/problems/combination-sum-ii/):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
class {public : vector <vector <int >> combinationSum2(vector <int >& candidates, int target) { vector <vector <int >> res; if (candidates.size() == 0 ) return res; sort(candidates.begin(), candidates.end()); if (target < candidates[0 ]) return res; vector <int > combination; backTrack(res, candidates, combination, target, 0 ); return res; } private : void backTrack (vector <vector <int >>& res, vector <int > candidates, vector <int > combination, int target, int begin) { if (target < 0 ) return ; else if (target == 0 ) { res.push_back(combination); return ; } for (int i = begin; i < candidates.size() && target >= candidates[i]; i++) { if (i == begin || candidates[i] != candidates[i - 1 ]) { combination.push_back(candidates[i]); backTrack(res, candidates, combination, target - candidates[i], i + 1 ); combination.pop_back(); } } } };
6.组合总和III(不能重复使用相同的元素)(https://leetcode.com/problems/combination-sum-iii/):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
class Solution {public : std ::vector <std ::vector <int > > combinationSum3(int k, int n) { std ::vector <std ::vector <int > > res; std ::vector <int > combination; combinationSum3(n, res, combination, 1 , k); return res; } private : void combinationSum3 (int target, std ::vector <std ::vector <int > > &res, std ::vector <int > &combination, int begin, int need) { if (!target) { res.push_back(combination); return ; } else if (!need) return ; for (int i = begin; i != 10 && target >= i * need + need * (need - 1 ) / 2 ; ++i) { combination.push_back(i); combinationSum3(target - i, res, combination, i + 1 , need - 1 ); combination.pop_back(); } } };
7.排列组合(https://leetcode.com/problems/permutations/):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
public List<List<Integer>> permute(int [] nums) { List<List<Integer>> list = new ArrayList<>(); backtrack(list, new ArrayList<>(), nums); return list; } private void backtrack (List<List<Integer>> list, List<Integer> tempList, int [] nums) { if (tempList.size() == nums.length){ list.add(new ArrayList<>(tempList)); } else { for (int i = 0 ; i < nums.length; i++){ if (tempList.contains(nums[i])) continue ; tempList.add(nums[i]); backtrack(list, tempList, nums); tempList.remove(tempList.size() - 1 ); } } }
8.排列II(包含重复项)(https://leetcode.com/problems/permutations-ii/):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
public List<List<Integer>> permuteUnique(int [] nums) { List<List<Integer>> list = new ArrayList<>(); Arrays.sort(nums); backtrack(list, new ArrayList<>(), nums, new boolean [nums.length]); return list; } private void backtrack (List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used) { if (tempList.size() == nums.length){ list.add(new ArrayList<>(tempList)); } else { for (int i = 0 ; i < nums.length; i++){ if (used[i] || i > 0 && nums[i] == nums[i-1 ] && !used[i - 1 ]) continue ; used[i] = true ; tempList.add(nums[i]); backtrack(list, tempList, nums, used); used[i] = false ; tempList.remove(tempList.size() - 1 ); } } }
9.回文分区(https://leetcode.com/problems/palindrome-partitioning/):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
public List<List<String>> partition(String s) { List<List<String>> list = new ArrayList<>(); backtrack(list, new ArrayList<>(), s, 0 ); return list; } public void backtrack (List<List<String>> list, List<String> tempList, String s, int start) { if (start == s.length()) list.add(new ArrayList<>(tempList)); else { for (int i = start; i < s.length(); i++){ if (isPalindrome(s, start, i)){ tempList.add(s.substring(start, i + 1 )); backtrack(list, tempList, s, i + 1 ); tempList.remove(tempList.size() - 1 ); } } } } public boolean isPalindrome (String s, int low, int high) { while (low < high) if (s.charAt(low++) != s.charAt(high--)) return false ; return true ; }
参考链接:
https://leetcode.com/problems/combination-sum/discuss/16502/A-general-approach-to-backtracking-questions-in-Java-(Subsets-Permutations-Combination-Sum-Palindrome-Partitioning)
https://leetcode.com/problems/combination-sum/discuss/16496/Accepted-16ms-c++-solution-use-backtracking-easy-understand .
42. Trapping Rain Water
1.原题:https://leetcode.com/problems/trapping-rain-water/description/
2.解法一(DP):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
int trap (vector <int >& height) { if (height == null) return 0 ; int ans = 0 ; int size = height.size(); vector <int > left_max(size), right_max(size); left_max[0 ] = height[0 ]; for (int i = 1 ; i < size; i++) { left_max[i] = max(height[i], left_max[i - 1 ]); } right_max[size - 1 ] = height[size - 1 ]; for (int i = size - 2 ; i >= 0 ; i--) { right_max[i] = max(height[i], right_max[i + 1 ]); } for (int i = 1 ; i < size - 1 ; i++) { ans += min(left_max[i], right_max[i]) - height[i]; } return ans; }
3.解法二(双指针):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
class Solution {public : int trap (int A[], int n) { int left=0 ; int right=n-1 ; int res=0 ; int maxleft=0 , maxright=0 ; while (left<=right){ if (A[left]<=A[right]){ if (A[left]>=maxleft) maxleft=A[left]; else res+=maxleft-A[left]; left++; } else { if (A[right]>=maxright) maxright= A[right]; else res+=maxright-A[right]; right--; } } return res; } };
参考链接:
https://leetcode.com/problems/trapping-rain-water/solution/
https://leetcode.com/problems/trapping-rain-water/discuss/17357/Sharing-my-simple-c++-code:-O(n)-time-O(1)-space
原文:大专栏 leetcode solution