Divide and Conquer

Question 1:

CS141-HW1

Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i,ai). n vertical lines are drawn such that the two endpoints of line i is at (i,ai) and (i,0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.

(1)Solve the problem with brute-force approach. What is the time complexity of this approach?

(2)Is there any better solution? If so, describe your solution with time complexity analysis.

Note: You may not slant the container and n is at least 2.

Solution 1 : Brute-force approach:

int find(int A[], int left ,int right){
    int maxArea = 0;
    int area;
    for(int i = left; i <= right; i++){
        for(int j = i + 1; j <= right; j++){
            area = (j - i) * min(A[i],A[j]);
            maxArea = max(area, maxArea);
        }
    }
    return maxArea;
}

Explanation: Area equals short line length multiply the width(area = length ∗ width). Outer for loop is the left line position, inner for loop is the right line position. First left starts at 0 and right starts at 1 (left + 1). Find the minimum length between the current two lines and set the value in min length. Then calculate current area and compare it with max area. If area is bigger than max area. Put the new value into max value. The two loop will calculate all possible results and compare them with max area. If result bigger than max value then update the max value. After the iteration terminates, max area’s value is the final result.

Complexity: Algorithm has two nested loop. Outer loop executes n − 1 times. In- ner loop depends on the value of left and executes (n − left) times. Total costs (n−1)∗((n−1)+(n−2)+...+1)<n2 ∈O(n2).

Solution 2:

int find2(int A[],int left ,int right){
    int maxArea = 0;
    int area = 0;
    int minHigh = 0;
    while(left < right){
        int width = right - left;
        if(A[left] > A[right]){
            minHigh = A[right--];
        }else{
            minHigh = A[left++];
        }
        area = width * minHigh;
        maxArea = max(area, maxArea);
    }
    return maxArea;
}

Explanation: area = minLength ∗ width and width = right − lef t. Now algorithm starts at biggest width(when right = n − 1 and lef t = 0, right − left = n − 1). Every iteration the width minus 1 so at this strategy algorithm only has to focus on the minimum of left and right. If left length < right length (a[left] < a[right]), then left moves to right one step. Otherwise right moves to right one step. Max value only depends on the minimum length, so we only move the minimum value’s position. The algorithm will calculate every width’s max area and compare them. Return the biggest one.

Complexity: Algorithm only has one loop and it executes (n − 1) times. So total algorithm costs O(n).

Solution 3:

int find3(int A[], int left, int right){
    if(left >= right){
        return 0;
    }
    int area = (right - left) * min(A[left],A[right]);
    if(A[left] > A[right]){
        return max(area,find(A, left, right - 1));
    }else{
        return max(area,find(A,left + 1, right));
    }
}

Explanation: Every time algorithm will calculate the current width’s max value and call the same function but in a smaller scale until left ≥ right. Compare the current max value with the smaller scale’s max value and return it.

Complexity: Algorithm executes (n − 1) times and costs O(n).

 

Question 2: (Easy)

Leetcode 169. Majority Element 

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.

Input: [3,2,3]
Output: 3

Input: [2,2,1,1,1,2,2]
Output: 2

Solution 1 : Sort

    int majorityElement(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        return nums[nums.size()/2];
    }

Complexity: Sort() costs O(nlogn). So total algorithm costs O(nlogn).

Solution 2: Divide and Conquer

int countNum(int* nums, int num,int left,int right){
    int count = 0;
    for(int i = 0; i < right - left + 1; i++){
        if(nums[i] == num)
            count++;
    }
    return count;
}

int findElement(int *nums,int left,int right){
    if(left == right)
        return nums[left];
    int mid = (right - left) / 2 + left;
    int leftNum = findElement(nums, left, mid);
    int rightNum = findElement(nums, mid + 1,right);
    
    if(leftNum == rightNum){
        return leftNum;
    }
    
    int leftCount = countNum(nums,leftNum,left,mid);
    int rightCount = countNum(nums,rightNum,mid + 1,right);
    return leftCount > rightCount ? leftNum : rightNum;
}

int majorityElement(int *nums,int numsSize){
    return findElement(nums,0,numsSize - 1);
}

Solution 3 (best way) : Boyer-Moore Voting Algorithm

int majorityElement3(int *nums,int numsSize){
    int count = 0;
    int result;
    for(int i = 0; i < numsSize; i++){
        if(count == 0){
            result = nums[i];
            count = 1;
        }else if(result != nums[i]){
            count--;
        }else{
            count++;
        }
    }
    return result;
}

Explanation: Majority element must more than (n/2) so if we use 'count' to calculate how many times it shows, 'count' must >= 1. such as( +1 -1 +1 -1 +1 ....). (+1) times always more than (-1) times at least once.

Complexity: O(n)

 

Question 3: (Easy)

Leetcode 53. Maximum Subarray

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

Solution 1 : Divide and Conquer

int findMidMax(int *nums,int left,int mid,int right){
    int result = nums[mid];
    int now = nums[mid];
    for(int i = mid + 1; i <= right; i++){
        now = now + nums[i];
        result = max(now,result);
    }
    now = result;
    for(int j = mid - 1; j >= left; j--){
        now = now + nums[j];
        result = max(result,now);
    }
    return result;
}

int findMax(int*nums,int left,int right){
    if(left >= right)
        return nums[left];
    
    int mid = (right - left) / 2 + left;
    int leftMax = findMax(nums, left, mid - 1);
    int rightMax = findMax(nums, mid + 1,right);
    int midMax = findMidMax(nums,left,mid,right);
    
    int maxresult = max(midMax,max(leftMax,rightMax));
    return maxresult;
}

int maxSubArray(int* nums, int numsSize){
    return findMax(nums,0,numsSize - 1);
}

Note: Please pay attention to the findMidMax() because it has to keep " now = result(current max_value) "when transfer to another side.

Complexity: O(nlogn)

Solution 2 :  best way

int maxSubArray2(int *nums,int numSize){
    int res = INT_MIN;
    int curSum = 0;
    for(int i = 0; i < numSize; i++){
        curSum = max(curSum + nums[i], nums[i]);
        res = max(res,curSum);
    }
    return res;
}

Explanation: Every iteration if curSum + nums[i] < nums[i] (curSum must < 0), restart at subArray(i,numSize). Then compare the new curSum with the old sum(res). The res will be the answer.

Complexity: O(n)

 

Question 4 : (Medium)

Leetcode 932. Beautiful Array

For some fixed N, an array A is beautiful if it is a permutation of the integers 1, 2, ..., N, such that:

For every i < j, there is no k with i < k < j such that A[k] * 2 = A[i] + A[j].

Given N, return any beautiful array A.  (It is guaranteed that one exists.)

Input: 4
Output: [2,1,4,3]

Input: 5
Output: [3,1,2,5,4]

Solution: Divide and Conquer

vector<int> beautifulArray(int n){
    if(n == 1){
        return {1};
    }

    
    vector<int> odd = beautifulArray((n + 1) / 2);
    vector<int> even = beautifulArray(n / 2); 

    vector<int> newArray;
    for(int i = 0; i < odd.size(); i++){
        newArray.push_back(odd[i] * 2 - 1); 
    }
    for(int i = 0; i < even.size(); i++){
        newArray.push_back(even[i] * 2); 
    }
    return newArray;
}

Explanation: We have to know that if Array(N) is a beautiful array then Array(N*2-1) is an odd beautiful array; Array(N*2) is an even beautiful array. So now we want to make Array(N) be a beautiful array. We divide the question to two part. Left part is an odd beautiful array. ( x*2-1 = N --> x = (N+1)/2 ). Right part is an even beautiful array. (x*2 = N --> x = 2*N). Then get the left part and right part together.

Compare: This question we don't divide into "left/mid/right" but into "odd/even". We make left-part to be odd-part ((N + 1) / 2),right-part to be even-part ( N / 2). This is the biggest difference with the above questions.

 

Question 5 : (Medium)

Leetcode 241. Different Ways to Add Parentheses

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 *.

Input: "2-1-1"
Output: [0, 2]
Explanation: 
((2-1)-1) = 0 
(2-(1-1)) = 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

Solution 1 : Divide and Conquer

vector<int> diffWaysToCompute(string input) {
    vector<int> res,left,right;
    // enum every possible seperate situation.
    for(int i = 0; i < input.size(); i++){
        if(input[i] == '+' || input[i] == '-' || input[i] == '*'){
            left = diffWaysToCompute(input.substr(0,i));
            right = diffWaysToCompute(input.substr(i+1));
        }
    // left and right are all possible numbers.
    // So each number in left has to operate with each number in right.
        for(int j = 0; j < left.size(); j++){
            for(int k = 0; k < right.size(); k++){
                if(input[i] == '+')
                    res.push_back(left[j] + right[k]);
                else if(input[i] == '-')
                    res.push_back(left[j] - right[k]);
                else if(input[i] == '*')
                    res.push_back(left[j] * right[k]);
            }
        }
    }
    // if only have one number in "input", res will be empty. 
    // So put this number into res.
    if(res.empty())
        res.push_back(stoi(input));
    return res;
}

Explanation:  First 'for' loop seperates every possible position which can divide the formula to two parts. Left-part and Right-part are both set with all possible results. So the two inner 'for' loop we have to calculate each possible result in left and right. When loop ends, if res still be empty that means the 'input' is only a single number. So transfer the string to integer and put it into res.

Solution 2 : Use HashMap to store the result. ( Reduce repetitive computation)

unordered_map <string, vector<int>> map;
vector<int> diffWaysToCompute2(string input) {
    if(map.count(input))
        return map[input];
    vector<int> res,left,right;

    for(int i = 0; i < input.size(); i++){
        if(input[i] == '+' || input[i] == '-' || input[i] == '*'){
            left = diffWaysToCompute2(input.substr(0,i));
            right = diffWaysToCompute2(input.substr(i+1));
        }

        for(int j = 0; j < left.size(); j++){
            for(int k = 0; k < right.size(); k++){
                if(input[i] == '+')
                    res.push_back(left[j] + right[k]);
                else if(input[i] == '-')
                    res.push_back(left[j] - right[k]);
                else if(input[i] == '*')
                    res.push_back(left[j] * right[k]);
            }
        }
    }

    if(res.empty())
        res.push_back(stoi(input));
    map[input] = res;
    return res;
}

Explanation: Add HashMap 'map' to store the previous results. If there already have result in map, just return it. Don't remember value the map[input] with res at the end of every step.

 

Question 6 : (Medium)

Leetcode 215. Kth Largest Element in an Array

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.

Input: [3,2,1,5,6,4] and k = 2
Output: 5

Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4

Solution 1 : sort the array

int findKthLargest(vector<int>& nums, int k) {
    sort(nums.begin(),nums.end());
    return nums[nums.size() - k];
}

Explanation: Sort the array and find K-th largest element.

Complexity: O(nlogn)

Solution 2 : Divide (Using the idea of quick-sort)

int partition(vector<int> &nums, int left, int right){
    int pivot = nums[right];
    int more = left - 1;
    int less = right;
    while(left < less){
        if(nums[left] > pivot){
            swap(nums[++more],nums[left++]);
        }else if(nums[left] < pivot){
            swap(nums[left],nums[--less]);
        }else{
            left++;
        }
    }
    swap(nums[right],nums[less]);
    return less;
}

int findKthLargest2(vector<int>& nums, int k) {
    int left = 0;
    int right = (int)nums.size() - 1;
    int Pivot = INT_MIN;
    while(1){
        Pivot = partition(nums, left, right);
        if(Pivot == k - 1){
            return nums[Pivot];
        }
        if(Pivot > k - 1){
            right = Pivot - 1;
        }else{
            left = Pivot + 1;
        }
    }
}

Explanation: If we use the idea of quick-sort and make left-part be the larger and right-part be the smaller. Then if pivot > k -1 that means the answer is in the right-part, otherwise in the left-part. If pivot == k - 1 then return the nums[pivot].

Note: Quick-sort partition is difficult for me. Try more times!!!!!

 

Question 7 : (Medium) 

Leetcode 240. Search a 2D Matrix II

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.
[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

Given target = 5, return true.

Given target = 20, return false.

Solution : search from right-top to left-bottom

bool searchMatrix(vector<vector<int>>& matrix, int target) {
    //if [[]] , make sure that we won't access matrix[0];
    if(matrix.size() == 0){
        return false;
    }
    int hang = (int)matrix.size() - 1;
    int lie = (int)matrix[0].size() - 1; 
    //(m,n) -> matrix[m][n]
    int m = 0;
    int n = lie;

    //start -> matrix[0][lie];
    //end -> matrix[hang][0];
    while(m <= hang && n >= 0){
        if(matrix[m][n] > target){
            n--;
        }else if(matrix[m][n] < target){
            m++;
        }else{
            return true;
        }
    }
    return false;
}

Explanation:  Search from right-top to left-bottom. If matrix[m][n] > target, go left. If matrix[m][n] < target, go down. If equal, return true.

Complexity: O(m+n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值