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


鏈接:https://leetcode.com/tag/divide-and-conquer/

 

【4】Median of Two Sorted Arrays 

【23】Merge k Sorted Lists 

 

【53】Maximum Subarray (2019年1月23日, 谷歌tag復習)

最大子段和。

題解:

follow up 是divide and conquer

If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle. 

 

【169】Majority Element 

 

【215】Kth Largest Element in an Array (2018年12月11日,wiggle sort 專題,需要復習)

用 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 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM