Divide and Conquer
分治演算法
通常需要三個步驟
1.將原問題分解為一組子問題,每個子問題都與原問題型別相同,但是比原問題的規模小
2.遞迴求解這些子問題
3.將子問題的求解結果恰當合併,得到原問題的解
Given an integer array nums
, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
思路:
將陣列分為左半邊,中間元素,右半邊。左半邊陣列中的最大和連續陣列,右半邊陣列中的最大和連續陣列,包含中間元素的當前陣列的最大和連續陣列,這三者當中和最大的陣列就是當前陣列的最大和連續陣列。
class Solution { public: int getmax(vector<int>& nums, int l, int r){ if(l==r){ return nums[l]; } int c = (l+r)/2; int maxl = getmax(nums,l,c); int maxr = getmax(nums,c+1,r); int maxll = INT_MIN,maxrr=INT_MIN,ll=0,rr=0; //中間子序列左右半部分的最大值 for(int i = c;i>=l;i--){ ll+=nums[i]; maxll = max(maxll,ll); } for(int i = c+1;i<=r;i++){ rr+=nums[i]; maxrr = max(maxrr,rr); } int maxc = maxll+maxrr; int maxnum = max(max(maxr,maxl),maxc); return maxnum; } int maxSubArray(vector<int>& nums) { return getmax(nums,0,nums.size()-1); } };
Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋
times.
You may assume that the array is non-empty and the majority element always exist in the array.
思路:
將陣列一分為二,分別求出兩邊的眾數。合併時,如果兩邊眾數一致,那麼合併後的眾數不變。如果不一致,就對兩個數分別計算,比較數量多少,選出一個眾數(如果相等,規定取一個,並不會影響最終的結果)。
class Solution {
public:
int majorityElement(vector<int>& nums) {
return handle(nums, 0, nums.size()-1);
}
int count(vector<int>& nums, int l, int r, int num) {
int c = 0;
for(int i = l; i <= r; i++)
if(nums[i] == num)
c++;
return c;
}
int handle(vector<int>& nums, int l, int r) {
if(l == r)
return nums[l];
int m = (l+r)/2;
int l_max = handle(nums, l, m);
int r_max = handle(nums, m+1, r);
if(l_max == r_max)
return l_max;
int l_count = count(nums, l, r, l_max);
int r_count = count(nums, l, r, r_max);
return l_count > r_count?l_max:r_max;
}
};
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
思路:
從陣列中任意選定一個數v,將陣列劃分為三個部分:1.比v小的數,2.等於v的數,3.大於v的數, 如果k小於等於3的長度,就說明要找的元素在3那一段,如果k大於3的長度,但小於等於3+2的長度,說明要找的元素就是v,如果k大於3+2的長度,說明要找的元素在1那一段,之後就等價於在1裡找第k-(2+3的長度)大的數了。
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
return handle(nums, k, 0, nums.size()-1);
}
void exchange(vector<int>& nums, int i ,int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
int handle(vector<int> &nums, int k, int l, int r) {
int lt = l, gt = r, v = nums[l];
for(int i = l; i <= r; i++)
if(nums[i] < v)
exchange(nums, lt++, i);
for(int i = r; i >= lt; i--)
if(nums[i] > v)
exchange(nums, gt--, i);
if(r-gt >= k)
return handle(nums, k, gt+1, r);
if(r-gt < k && r-lt+1 >= k)
return nums[lt];
if(r-lt+1 < k)
return handle(nums, k-(r-lt+1), l, lt-1); //確定好 +1 -1
}
};
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
- Integers in each row are sorted in ascending from left to right.
- Integers in each column are sorted in ascending from top to bottom
思路:
對於一個矩陣,左上角的數最小,右下角的數最大。將一個矩陣按田字分,可以快速排除不含該數字的子矩陣,然後對各個子問題進行進一步求解。
struct point {
int x;
int y;
};
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.size() == 0 || matrix[0].size() == 0)
return false;
return handle(matrix, {0,0}, {matrix[0].size()-1,matrix.size()-1}, target);
}
bool handle(vector<vector<int>>& matrix, point l, point r, int target) {
if(matrix[l.y][l.x] > target || matrix[r.y][r.x] < target)
return false;
// if(matrix[l.y][l.x] == target || matrix[r.y][r.x] == target)
// return true;
if(l.x == r.x && l.y == r.y)
return matrix[l.y][l.x] == target;
point m = {(l.x+r.x)/2,(l.y+r.y)/2};
if(handle(matrix,l,{m.x,m.y},target))
return true;
if(m.x+1 < matrix[0].size() && handle(matrix,{m.x+1,l.y},{r.x, m.y},target))
return true;
if(m.y+1 < matrix.size() && handle(matrix,{l.x,m.y+1},{m.x,r.y},target))
return true;
if(m.x+1 < matrix[0].size() && m.y+1 < matrix.size() && handle(matrix,{m.x+1,m.y+1},r,target))
return true;
return false;
}
};
Given a string of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. The valid operators are +
, -
and *
.
思路:
對於一個包含n個運算子的算式,有n種方法使用括號將其變為兩個”數“之間的運算,對於這兩個“數“,又各自有他們包含的運算子的數量這麼多種方法再進行相同的分解,最終分解為不包含運算子的算式。然後再將兩邊的所有可能進行運算,得到所有可能的結果。
class Solution {
public:
vector<int> diffWaysToCompute(string input) {
vector<int> v = handle(input);
std::sort(v.begin(),v.end());
return v;
}
vector<int> handle(string input) {
if(input.find('+') == string::npos && input.find('-') == string::npos && input.find('*') == string::npos)
return {atoi(input.c_str())};
vector<int> res;
for(int i = 0; i < input.length(); i++) {
if(input[i] == '+' || input[i] == '-' || input[i] == '*') {
vector<int> left = handle(input.substr(0,i));
vector<int> right = handle(input.substr(i+1,input.length()-i));
for(auto k:left)
for(auto j:right) {
if(input[i] == '+') {
res.push_back({k+j});
} else if(input[i] == '-') { // 注意i,j重複使用
res.push_back({k-j});
} else if(input[i] == '*') {
res.push_back({k*j});
}
}
}
}
return res;
}
};