【LeetCode】分治法 divide and conquer (共17題)

連接: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 };
View Code

 

【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 };
View Code

 

【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 };
View Code

 

【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 };
View Code

 

【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 };
View Code

  

【514】Freedom Trail 

【903】Valid Permutations for DI Sequence 

【932】Beautiful Array

相關文章
相關標籤/搜索