【算法】和爲S的連續正數序列(多解)

這個題很經典,我只會兩種做法,然後在網上看了別的大佬的新穎解法(我想不到的0.0),也算作是個學習吧。

題目描述
輸入一個正數s,打印出所有和爲s的連續正數序列(至少含有兩個數)。

例如輸入15,由於1+2+3+4+5=4+5+6=7+8=15,所以結果打印出3個連續序列1~5、4~6和7~8。

樣例

輸入:15
輸出:[[1,2,3,4,5],[4,5,6],[7,8]]

方法一:暴力DFS(我的笨解法,沒有什麼值得注意的地方)

class Solution {
public:

    void dfs(vector<vector<int>>& vv,vector<int> v, int target, int sum)
    {
        if(target <= sum)
        {
            if(target == sum)
            {
                vv.push_back(v);
            }
            return;
        }
        for(int i = 1; i < target; i++)
        {
            if(v.empty() || (!v.empty() && i == v.back() + 1))
            {
                v.push_back(i);
                dfs(vv,v,target,sum + i);
                v.pop_back();
            }
        }
    }
    vector<vector<int> > findContinuousSequence(int sum) {
        vector<vector<int>> vv;
        vector<int> v;
        if(sum == 0)
            return vv;
        dfs(vv,v,sum,0);
        return vv;
    }
};

方法二 暴力枚舉 (我的O(N*N)的笨方法)

思路:
設置兩個指針i和j,j再去遍歷數組後面的數字,看是否能加到sum, j和 j + n分別指向連續正數序列的起始和終止
用s表示當前連續正數序列的和,即s=j+(j+1)+…+j + n <= sum ?
看是否能湊出sum的值。

時間複雜度:O(N*N)
空間複雜度:O(N)


class Solution {
public:
    vector<vector<int> > findContinuousSequence(int sum) {
        vector<vector<int> > ans;
        for(int i = 1; i < sum; i++) {
            int res = 0;
            vector<int> tmp;
            for(int j = i; j < sum; j++)  {
                tmp.push_back(j);
                res += j;
                if(res >= sum) break;
            }
            if(res == sum) ans.push_back(tmp);
        }
        return ans;
    }
};

方法三: 利用求和公式

思路:利用連續數組求和公式,Sn = (首項 + 尾項)*項數 / 2 。所以利用雙指針i,j 求的解。

class Solution {
public:
    vector<vector<int> > findContinuousSequence(int sum) {
        vector<vector<int>> res;
        vector<int> path;
        for(int i = 1, j = 2; j < sum && i < j; j) {
            int ans = (i + j) * (j - i + 1) / 2; 
            if ( ans == sum){	//如果相同就加入。
                int k = i;
                while(k <= j)
                    path.push_back(k++);
                res.push_back(path);
                path.clear();
                i ++, j ++;//兩個指針同時往後移。
            }
            else if ( ans < sum) {//如果比較小,j就往後移動。
                j ++;
            }
            else 
                i ++;//否則i往後移動
        }
        return res;

    }
};


方法四:雙指針

思路:

設置兩個指針i和j,分別指向連續正數序列的起始和終止

用s表示當前連續正數序列的和,即s=i+(i+1)+…+js=i+(i+1)+…+j
以i遞增的方式遍歷整個序列(1到n),代表查找以i開頭的時候結尾j應該是多少。當s<sums<sum說明j應該往後移動,當s=sums=sum說明滿足題意,當s>sums>sum說明向後走即可。
注意上述遍歷過程中,s=sums=sum的情況下不需要把j往前移動,原因是當進入下一個循環前s−=is−=i,即(i+1)到j的和肯定小於sum。

class Solution {
public:
    vector<vector<int> > findContinuousSequence(int sum) {
        vector<vector<int>> res;
        for (int i = 1, j = 1, s = 1; i <= sum; i ++ )
        {
            while (s < sum) j ++, s += j;
            if (s == sum && j > i)
            {
                vector<int> line;
                for (int k = i; k <= j; k ++ ) line.push_back(k);
                res.push_back(line);
            }
            s -= i;
        }
        return res;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章