虽然不是每天都刷,但还是不想改标题,(手动狗头
题目及解法来自于力扣(LeetCode),传送门。
算法(78):
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
这道题我自己其实一开始没有什么比较好的想法,希望能用循环来解决,但写着写着发现处理情况有些多/循环嵌套次数过多。来一起看看网友给的解法吧,回溯法:
public class Solution {
//回溯法
private IList<IList<int>> res;
private void find(int[] nums, int begin, IList<int> pre)
{
// 没有显式的递归终止
res.Add(new List<int>(pre));// 注意:这里要 new 一下
for (int i = begin; i < nums.Length; i++)
{
pre.Add(nums[i]);
find(nums, i + , pre);
pre.RemoveAt(pre.Count - );// 组合问题,状态在递归完成后要重置
}
}
public IList<IList<int>> Subsets(int[] nums)
{
int len = nums.Length;
res = new List<IList<int>>();
if (len == )
{
return res;
}
IList<int> pre = new List<int>();
find(nums, , pre);
return res;
}
}
回溯算法的一般是这样一个过程:在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索的方法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
我们回到上面的方法,重点其实在4~14行这个find方法里。这个方法其实只做了两件事,1.向输出结果的res中添加集合。2.准备下一组数据。我们重点看看怎么准备下一组数据。在for循环中定义了起始的索引,终止条件是遍历整个原集合。注意10~12行,这里其实是把集合中数组的个数分隔开来。嗯,换一种表达方式是,假如我们以[0,1,2]这个集合举例。它的所有子集是:[],[0],[0,1],[0,1,2],[0,2],[1],[1,2],[2]. (注意这里的顺序,上面的代码输入也是按这个顺序的)子集中的空集是在第一层调用find时添加进结果集合中的,子集中个数为1的集合是在第二层调用find时添加进去的。理解了这里,也就方便理解了为什么要有12行 pre.RemoveAt(pre.Count - 1);