这道题算中等难度真的不为过。

要注意,其结果有一个隐含的要求,每一项都是有序的(从小到大)。

看那例子:n=4, k=2,我们可以根据规律列个表:

pos 0 pos 1
1 2
1 3
1 4
2 3
2 4
3 4

两个循环貌似就可以搞定:

for (int i=1; i<=n; ++i)
    for (int j=i+1; j<=n; ++j)
        retv.push_back({i, j});

但这属于 k=2 的特殊情况。如果 k = 3 呢。

pos 0 pos 1 pos 2
1 2 3
1 2 4
1 3 4
2 3 4

貌似就需要三个循环了:

for (int i=0; i<=n; ++i)
    for (int j=i+1; j<=n; ++j)
        for (int k=j+1; k<=n; ++k)
            retv.push_back({i, j, k});

这不扯了么?k 等于多少,就要循环几次。有没有能够控制循环次数的循环?

有,但你可以想象这样的循环有多么不近人情,而且,这不是典型的递归的干活么。。。嗯,就是递归。(为啥需要递归,这时候真需要!)

retv.push_back({i, j, k}), 这样的东西无论如何也要改,可以先将 retv 的每一个 vec 初始化为 k 长度,然后使用 vec[当前循环次数] = 当前循环参数;

void combine(int i, int n, int k)
{
    while (i<=n)
    {
        vec[vec.size() - k] = i++;
        if (k == 1) retv.push_back(vec);
        else combine(i, n, k-1);
    }
}

这就是核心算法了,然后调此递归即可:combine(1, n, k);


我的AC使用的是一种”倒着填”的类似方案,我觉得要更简洁一些。但写思路的时候,发现上面那样写,更加符合从 多次循环 到 递归 的思想。更近人情。

#include <vector> 
#include <functional>

using std::vector; using std::function;

class Solution {
public:
    vector<vector<int> > combine(int n, int k) {
        vector<vector<int>> retv;
        vector<int> vec(k);
        function<void(int, int, int)> combine = [&](int i, int n, int k) {
            for (int j=k-1; i>0; j ? combine(i, n, j) : retv.push_back(vec))
                vec[j] = i--;
        };
        combine(n,n,k);
        return retv;
    }
};

LeetCode Direct Link