1. 程式人生 > >演算法設計與分析第二次作業

演算法設計與分析第二次作業

leetcode 241

題目描述: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 *. 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

題目要求找出一個計算式加括號後可以得到的所有結果,此問題的複雜性在於如果要考慮所有的可能性的話其複雜度是以階乘向上增長的,有n個運算子的時候所有的可能性就有n!種,如果使用暴力窮舉的話其運算量在運算子較多時就已經在一個很恐怖的數量級了,所以必須對其進行簡化。其實很容易發現在這些階乘的可能性中,其中存在大量的重複,不難看出所有的計算式都可以寫成(substring1)op(substring2)這樣的形式,而其實無論兩個子字串中的運算子的運算順序是怎麼樣的,其實最終答案都是一樣的,而暴力窮舉的話這部分就造成了大量的重複,所以這樣就可以想到這題可以用分治法進行處理,對於每一個運算子都以它為分隔符,然後分成左右兩個部分,左右兩個部分的值又可以遞迴求解,直至剩下一個值,左右部分的值確定後最後的結果就可以確定

複雜度分析: 假設有n個運算子,其中每次分割最壞的情況,也就是切割第一個或者是最後一個運算子,其滿足關係式T(n)=T(n1)+O(1) , 不難看出其複雜度為O(n),又有n個運算子,所以總的複雜度為O(n2) ,實際上在切割最中間的運算子時因為有T(n)=2T(n/2)+O(1) , 根據大師定理複雜度為O(logn),實際的複雜度應該會更小

程式碼如下:

class Solution {
public:
    vector<int> diffWaysToCompute(string input) {
        vector
<int>
ans; for(int i = 1; i < input.length(); i++){ if(!isdigit(input[i])){ string left = input.substr(0,i); string right = input.substr(i+1); vector<int> leftPossible = diffWaysToCompute(left); vector<int> rightPossible = diffWaysToCompute(right); for(auto j : leftPossible){ for(auto k : rightPossible){ if(input[i] == '+') ans.push_back(j+k); else if(input[i] == '-') ans.push_back(j-k); else ans.push_back(j*k); } } } } if(ans.empty()) ans.push_back(atoi(input.c_str())); return ans; } };

leetcode240. 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. Example:

Consider the following matrix:

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

這題要求判斷在一個對每行和每列都排好序的矩陣中是否存在某個數,一開始看到題目很自然的想法就是類似進行二分查詢,就是按行和列的元素個數進行斜對角線的搜尋,但是其實仔細分析後其實發現即使target大於某個對角線上的值,也只能說明這個值左上方都小於target,還是有一個反L型的區域需要搜尋,而即使target小於某個對角線上的值也不能說明target一定在這個元素的左上方。非要用這種方法做也可以,就把那個L型的區域切割成三個子矩陣就好了,其複雜度滿足T(a)=3T(a/4)+O(1),其中a = mn, 由大師定理可以得到複雜度為O(alog43),其實比掃描整個矩陣所花費的複雜度mn並沒有快非常多,所以要找出更快的演算法必須改變思路

其實這題的突破點就在於行和列都是排好序的,如果target比一行中的元素還要大,那它一定不在這行中,列也是同理,所以其實我們可以從矩陣的左下方開始向右上方掃描,首先我們的範圍是整個矩陣,但是如果左下方的元素比target大,那麼由於左下方的元素是這一列中最大的,所以可以把最左邊的列排除,而如果左下方的元素比target小,那麼由於左下方的元素是這一行中最小的,所以可以把最下面的一列排除,這樣一直掃描下去,知道搜尋範圍為空或者是找到target,這樣做一次可以排除一行或者一列,複雜度為O(m+n),當資料量較大時明顯比之前的演算法複雜度要小(這一點假設m=n就可以看的比較明顯),程式碼如下:

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty() || matrix[0].empty())
            return false;
        int i = matrix.size()-1, j = 0;
        while(i >= 0 && j < matrix[0].size()){
            if(matrix[i][j] > target)
                i--;
            else if(matrix[i][j] < target)
                j++;
            else return true;
        }
        return false;
    }
};