连接:https://leetcode.com/tag/divide-and-conquer/git
【4】Median of Two Sorted Arrays 算法
【23】Merge k Sorted Lists 数组
【53】Maximum Subarray (2019年1月23日, 谷歌tag复习)app
最大子段和。dom
题解:ide
follow up 是divide and conquerui
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle. lua
【169】Majority Element spa
【215】Kth Largest Element in an Array (2018年12月11日,wiggle sort 专题,须要复习)code
用 O(n) 的时间复杂度找到数组中第 K 大的元素。重复元素也计入 K。
题解:本题能够用 heap 解答,时间复杂度是 O(nlogK)。其实还能够用 quick select 解答(也就是快排的partition(2-way partition)),平均复杂度是 O(n),最坏是 O(n^2)。为了搞平均,一开始要把数组 random_shuffle 一下,尽量避免 worst case。
1 class Solution { 2 public: 3 int findKthLargest(vector<int>& nums, int k) { 4 random_shuffle(nums.begin(), nums.end()); 5 const int n = nums.size(); 6 int start(0), end(n-1), index(n-k); 7 while (start < end) { 8 int idx = partition(nums, start, end); 9 if (idx < index) { 10 start = idx + 1; 11 } else if (idx > index) { 12 end = idx - 1; 13 } else { 14 return nums[idx]; 15 } 16 } 17 return nums[start]; 18 } 19 int partition(vector<int>& nums, int start, int end) { 20 int pivot = start; 21 while (start < end) { 22 while (nums[start] <= nums[pivot]) { 23 start++; 24 } 25 while (nums[end] > nums[pivot]) { 26 end--; 27 } 28 if (start > end) {break;} 29 swap(nums[start], nums[end]); 30 } 31 swap(nums[end], nums[pivot]); 32 return end; 33 } 34 35 };
【218】The Skyline Problem
【240】Search a 2D Matrix II
【241】Different Ways to Add Parentheses (2018年11月15日,算法群)
给了一个字符串算式,里面含有 “+”,“-”,“*” 这三种运算符,能够在算式的任何一个地方加括号,整个算式能加的括号数不限。问这个算式全部可能的答案。
Example 1: Input: "2-1-1" Output: [0, 2] Explanation: ((2-1)-1) = 0 (2-(1-1)) = 2 Example 2: Input: "2*3-4*5" Output: [-34, -14, -10, -10, 10] Explanation: (2*(3-(4*5))) = -34 ((2*3)-(4*5)) = -14 ((2*(3-4))*5) = -10 (2*((3-4)*5)) = -10 (((2*3)-4)*5) = 10
题解:咱们能够在任意地方加括号,每一个运算符的两边均可以加个括号当作是一个子问题,先把子问题的全部解求出来,而后把两个子问题的解集合作笛卡尔积,造成大问题的解集合。
审题很重要,我一度觉得若是运算符是 ‘-’ 的话,那么后面的算式的 加号要变成减号,减号要变成加号,这个题意里面是没有的。
1 class Solution { 2 public: 3 vector<int> diffWaysToCompute(string input) { 4 return calPart(input); 5 } 6 vector<int> calPart(string s) { 7 const int n = s.size(); 8 if (record.find(s) != record.end()) {return record[s];} 9 //纯数字的状况 10 if (s.find("+") == string::npos && s.find("-") == string::npos && s.find("*") == string::npos) { 11 vector<int> ret{stoi(s)}; 12 return ret; 13 } 14 //含有运算符的状况 15 vector<int> res; 16 for (int i = 0; i < n; ++i) { 17 if (!isdigit(s[i])) { 18 string front = s.substr(0, i), back = s.substr(i+1); 19 vector<int> resFront = calPart(front), resBack = calPart(back); 20 for (auto f : resFront) { 21 for (auto b : resBack) { 22 int tempres = 0; 23 if (s[i] == '+') { 24 tempres = f + b; 25 } else if (s[i] == '-') { 26 tempres = f - b; 27 } else if (s[i] == '*') { 28 tempres = f * b; 29 } 30 res.push_back(tempres); 31 } 32 } 33 } 34 } 35 //sort(res.begin(), res.end()); 36 record[s] = res; 37 return res; 38 } 39 unordered_map<string, vector<int>> record; 40 };
【282】Expression Add Operators (2019年3月16日,dfs,打卡题)
Given a string that contains only digits 0-9
and a target value, return all possibilities to add binary operators (not unary) +
, -
, or *
between the digits so they evaluate to the target value.
Example 1: Input: num = "123", target = 6 Output: ["1+2+3", "1*2*3"] Example 2: Input: num = "232", target = 8 Output: ["2*3+2", "2+3*2"] Example 3: Input: num = "105", target = 5 Output: ["1*0+5","10-5"] Example 4: Input: num = "00", target = 0 Output: ["0+0", "0-0", "0*0"] Example 5: Input: num = "3456237490", target = 9191 Output: []
题解:若是本题只有加号和减号,那么就是一个直接的dfs。可是本题还有一个乘号,乘号的能够提早优先级运算。因此咱们须要保存最后一个单项式的值。
好比说咱们已经计算了 1 + 2 - 3 ___ 5 要填写 3 和 5 之间的符号的时候, 若是咱们想填个乘号,那么,咱们须要最后一项 -3,因此咱们用一个变量保存当前表达式的最后一个参数,做为 dfs 的参数。
1 class Solution { 2 public: 3 vector<string> addOperators(string num, int target) { 4 vector<string> res; 5 string temp; 6 dfs(num, temp, res, (long)target, 0, 0LL, 0LL); 7 return res; 8 } 9 void dfs(const string num, string temp, vector<string>& res, long target, int start, long curRes, long lastVal) { 10 if (start == num.size()) { 11 if (target == curRes) {res.push_back(temp);} 12 return; 13 } 14 for (int i = start; i < num.size(); ++i) { 15 string strCur = num.substr(start, i - start + 1); 16 if (strCur.size() > 1 && strCur[0] == '0') {break;} //leading zeros 17 long iCur = stol(strCur); 18 if (start == 0) { 19 dfs(num, strCur, res, target, i+1, iCur, iCur); 20 } else { 21 dfs(num, temp + "+" + strCur, res, target, i + 1, curRes + iCur, iCur); 22 dfs(num, temp + "-" + strCur, res, target, i + 1, curRes - iCur, -iCur); 23 dfs(num, temp + "*" + strCur, res, target, i + 1, curRes - lastVal + lastVal * iCur, lastVal * iCur); 24 } 25 } 26 } 27 };
【312】Burst Balloons
【315】Count of Smaller Numbers After Self (2019年2月12日,归并排序)
给了一个数组,要求返回一个数组,返回数组中的元素是原数组中每一个元素右边比它小的个数。
Input: [5,2,6,1]
Output: [2,1,1,0]
Explanation:
To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.
题解:这个题能够用线段数,树状数组,分治法来解。下面说一下分治法怎么解。
在每一轮中,咱们能够把整个数组分红左右两半,利用归并排序的思想,把左区间和右区间变的有序以后,这样咱们就能够把左区间内的任意一个元素nums[i],在右区间内用 lower_bound 找到有多少个元素小于它,而后把这个值加到res[i]上。
一共须要三个数组:nums, sorted, count。原来的数据存在nums, 归并排序后的数组存在sortedNums, count[i]对应的是nums[i]的 number of smaller elements to the right.
1 class Solution { 2 public: 3 vector<int> countSmaller(vector<int>& nums) { 4 const int n = nums.size(); 5 vector<int> sorted(nums.begin(), nums.end()), res(n, 0); 6 if (n == 0) {return res;} 7 mergesort(nums, sorted, 0, n-1, res); 8 return res; 9 } 10 void mergesort(vector<int>& nums, vector<int>& sorted, int begin, int end, vector<int>& res) { 11 if (begin == end) { return; } 12 int mid = (begin + end) / 2; 13 mergesort(nums, sorted, begin, mid, res); 14 mergesort(nums, sorted, mid + 1, end, res); 15 for (int i = begin; i <= mid; ++i) { 16 int value = nums[i]; 17 auto iter = lower_bound(sorted.begin() + mid + 1, sorted.begin() + end + 1, value); 18 res[i] += distance(sorted.begin() + mid + 1, iter); 19 } 20 //merge 21 vector<int> arr1(sorted.begin() + begin, sorted.begin() + mid + 1), 22 arr2(sorted.begin() + mid + 1, sorted.begin() + end + 1); 23 int p1 = 0, p2 = 0; 24 for (int idx = begin; idx <= end; ++idx) { 25 if (p1 == arr1.size()) { 26 sorted[idx] = arr2[p2++]; 27 } else if (p2 == arr2.size()) { 28 sorted[idx] = arr1[p1++]; 29 } else { 30 if (arr1[p1] < arr2[p2]) { 31 sorted[idx] = arr1[p1++]; 32 } else { 33 sorted[idx] = arr2[p2++]; 34 } 35 } 36 } 37 } 38 };
【327】Count of Range Sum (2019年2月14日,谷歌tag,归并排序)
给了一个数组nums,和一个范围 [lower, upper],返回有多少个子数组的和在这个范围以内。
题解:
【426】Convert Binary Search Tree to Sorted Doubly Linked List
【493】Reverse Pairs (2019年2月19日,归并排序)
给定一个数组,求它的逆序对个数。本题的逆序对的定义和别的题不一样:if i < j and nums[i] > 2*nums[j].
题解:咱们这个题目同315,用分治法解题。咱们把这整个数组分红两个区间,而后分别对这两个左右区间作归并排序。而后对右边区间的每个元素 nums[j], target = nums[j] * 2。而后用 upper_bound() 求出左区间内第一个比 target 大的元素,从这个元素开始 到左区间结束,这些元素都能和 target 组成逆序对。因此把这些元素加到结果上。
1 class Solution { 2 public: 3 typedef long long LL; 4 int reversePairs(vector<int>& nums) { 5 if (nums.empty()) {return 0;} 6 int n = nums.size(); 7 vector<LL> arr(n); 8 for (int i = 0; i < n; ++i) { 9 arr[i] = nums[i]; 10 } 11 divideAndConquer(arr, 0, n-1); 12 return res; 13 } 14 int res = 0; 15 void divideAndConquer(vector<LL>& nums, int start, int end) { 16 if (start == end) {return;} 17 int mid = start + (end - start) / 2; 18 divideAndConquer(nums, start, mid); 19 divideAndConquer(nums, mid + 1, end); 20 for (int j = mid + 1; j <= end; ++j) { 21 LL target = nums[j] * 2; 22 auto iter = upper_bound(nums.begin() + start, nums.begin() + mid + 1, target); 23 res += distance(iter, nums.begin() + mid + 1); 24 } 25 // print(nums, start, end); 26 inplace_merge(nums.begin() + start, nums.begin() + mid + 1, nums.begin() + end + 1); 27 // print(nums, start, end); 28 } 29 void print(vector<LL>& nums, int start, int end) { 30 for (int i = start; i <= end; ++i) { 31 printf("%lld ", nums[i]); 32 } 33 printf("\n"); 34 } 35 };
【514】Freedom Trail
【903】Valid Permutations for DI Sequence
【932】Beautiful Array