求集合 [1, 2] 的所有子集,即求出 [], [1], [2], [1, 2](通常 [] 空集合也被看作为子集之一)。观察图 1,可看到从根节点开始,每个叶子节点都有两个子节点:代表该节点是否选择加入下一个数字。

backtracking-subsets.png

图 2

backtracking-subsets.png

图 1

以下代码是使用递归的示例:

recursive(Integer[] input, Integer index, LinkedList<Integer> subset, List<List<Indeger>> result)>>)

subset 即子集的初始值是 [],此时 index = 0:

当 index = 0 时面临两种选择:是否加入 1;

当 index = 1 也面临两种选择,是否加入 2;

// 不选择当前数字 input[index]
recursive(input, index + 1, subset, result)
// 选择当前数字 input[index] **并继续下一步的选择**。
subset.add(input[index])
recursive(input, index + 1, subset, result)

在 index = 2 满足边界条件时开始执行递归出栈:

首先出栈的是 index = 2(满足边界条件),停止递归并返回 [];

然后出栈 index = 1,返回 [];同时在这一步选择:

subset.add(input[1]) // 返回 [2]

此外,请留意上述步骤:当选择数字以后,subsets = [1],并继续往前:

recursive(input, index + 1, [1], result)
// index = 2 时,满足边界条件返回

因为 subsets 在栈结构中是一个引用对象,在返回到上一个任务时其是被修改状态,应当移除其最后一个元素:

subset.removeLast();

backtracking-subsets.png

图 3