LeetCode刷题191203 --回溯算法

  虽然不是每天都刷,但还是不想改标题,(手动狗头

  题目及解法来自于力扣(LeetCode),传送门

算法(78)

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:
[
[3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

  这道题我自己其实一开始没有什么比较好的想法,希望能用循环来解决,但写着写着发现处理情况有些多/循环嵌套次数过多。来一起看看网友给的解法吧,回溯法:

 1 public class Solution {
 2     //回溯法
 3     private IList<IList<int>> res;
 4     private void find(int[] nums, int begin, IList<int> pre)
 5     {
 6         // 没有显式的递归终止
 7         res.Add(new List<int>(pre));// 注意:这里要 new 一下
 8         for (int i = begin; i < nums.Length; i++)
 9         {
10             pre.Add(nums[i]);
11             find(nums, i + 1, pre);
12             pre.RemoveAt(pre.Count - 1);// 组合问题,状态在递归完成后要重置
13         }
14     }
15     public IList<IList<int>> Subsets(int[] nums)
16     {
17         int len = nums.Length;
18         res = new List<IList<int>>();
19         if (len == 0)
20         {
21             return res;
22         }
23         IList<int> pre = new List<int>();
24         find(nums, 0, pre);
25         return res;
26     }
27 }

  回溯算法的一般是这样一个过程:在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索的方法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

  我们回到上面的方法,重点其实在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);

  

posted @ 2019-12-03 17:03  DogTwo  阅读(274)  评论(0编辑  收藏  举报