【算法】和为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;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章