给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
示例
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
解法
思路:回溯法,个人喜欢用栈,操作对象都是新添加(或上一步添加)的元素。
- 判断压入元素是否大于栈顶元素,避免数字的重复使用
- 压入元素,递归进行下一次压入
- 当栈中元素个数等于 k 时,说明一个组合已求解完成,将结果添加至列表中
- 回溯,将栈顶元素取出,进行下一分支的求解
- 重复上述步骤直至循环结束
public IList<IList<int>> Combine(int n, int k)
{
IList<IList<int>> res = new List<IList<int>>();
Stack<int> stack = new Stack<int>();
BackTrack(res, stack, n, k);
return res;
}
public void BackTrack(IList<IList<int>> res, Stack<int> stack, int n, int k)//回溯法
{
if (stack.Count == k) res.Add(stack.ToArray());
else
{
for (int i = 1; i <= n; i++)
{
int top = stack.Count == 0 ? 0 : stack.Peek();
if (top < i)
{
stack.Push(i);
BackTrack(res, stack, n, k);
stack.Pop();
}
}
}
}
优化:我们可以使用指针 first 指向循环开始的位置,无需Peek操作,这样可以减少判断,缩短执行时间。
public IList<IList<int>> Combine(int n, int k)
{
IList<IList<int>> res = new List<IList<int>>();
Stack<int> stack = new Stack<int>();
BackTrack(res, stack, 1, n, k);
return res;
}
public void BackTrack(IList<IList<int>> res, Stack<int> stack, int first, int n, int k)//回溯法
{
if (stack.Count == k) {res.Add(stack.ToArray());}
else
{
for (int i = first; i <= n; i++)
{
stack.Push(i);
BackTrack(res, stack, i + 1, n, k);
stack.Pop();
}
}
}