好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

代码随想录算法训练营第二十四天| 理论基础 77. 组合

 

理论基础 

      卡哥建议: 其实在讲解二叉树的时候,就给大家介绍过回溯,这次正式开启回溯算法,大家可以先看视频,对回溯算法有一个整体的了解。

     题目链接/文章讲解:https://programmercarl.com/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html

     视频讲解:https://www.bilibili.com/video/BV1cy4y167mM

 

 77. 组合  

       卡哥建议: 对着 在 回溯算法理论基础 给出的 代码模板,来做本题组合问题,大家就会发现 写回溯算法套路。 在回溯算法解决实际问题的过程中,大家会有各种疑问,先看视频介绍,基本可以解决大家的疑惑。 本题关于剪枝操作是大家要理解的重点,因为后面很多回溯算法解决的题目,都是这个剪枝套路。 

      题目链接/文章讲解:https://programmercarl.com/0077.%E7%BB%84%E5%90%88.html

      视频讲解:https://www.bilibili.com/video/BV1ti4y1L7cv

      剪枝操作:https://www.bilibili.com/video/BV1wi4y157er

     做题思路:

      暴力解法思路:当然是使用for循环,例如示例中k为2,很容易想到 用两个for循环;如果n为100,k为50呢,那就50层for循环!

     那么回溯法就用递归来解决嵌套层数的问题。 递归来做层叠嵌套(可以理解是开k层for循环),每一次的递归中嵌套一个for循环,那么递归就可以用于解决多层嵌套循环的问题了。回溯法解决的问题都可以抽象为树形结构(N叉树),用树形结构来理解回溯就容易多了。

 

        图中可以发现n相当于树的宽度,k相当于树的深度。图中每次搜索到了叶子节点,我们就找到了一个结果。

      相当于只需要把达到叶子节点的结果收集起来,就可以求得 n个数中k个数的组合集合。

     回溯法三部曲(见卡哥文章)

递归函数的返回值以及参数
回溯函数终止条件 单层搜索的过程

     path是一维数组,用来存放符合条件单一结果;result是二维数组,用来存放符合条件结果的集合。

     startIndex 就是防止出现重复的组合,在代码里是一个变量,从1开始。

     本题代码:

   1   class   Solution {
   2   private  :
   3      vector<vector< int >> result;  //   存放符合条件结果的集合 
  4      vector< int > path;  //   用来存放符合条件结果 
  5       void  backtracking( int  n,  int  k,  int   startIndex) {
   6           if  (path.size() ==  k) {
   7               result.push_back(path);
   8               return  ;
   9           }
  10           for  ( int  i = startIndex; i <= n; i++ ) {
  11              path.push_back(i);  //   处理节点 
 12              backtracking(n, k, i +  1 );  //   递归 
 13              path.pop_back();  //   回溯,撤销处理的节点 
 14           }
  15       }
  16   public  :
  17      vector<vector< int >> combine( int  n,  int   k) {
  18          result.clear();  //   可以不写 
 19          path.clear();    //   可以不写 
 20          backtracking(n, k,  1  );
  21           return   result;
  22       }
  23  }; 

     

      剪枝优化

     比如, n = 4,k = 4的话,那么第一层for循环的时候,从元素2开始的遍历都没有意义了,因为(2,3,4),不含4个数。 在第二层for循环,从元素3开始的遍历都没有意义了。

 

 

    优化过程如下(看卡哥文章举例):

已经选择的元素个数:path.size();

还需要的元素个数为: k - path.size();

在集合n中至多要从该起始位置 : n - (k - path.size()) + 1,开始遍历

     剪枝优化后整体代码如下:

   1   class   Solution {
   2   private  :
   3      vector<vector< int >>  result;
   4      vector< int >  path;
   5       void  backtracking( int  n,  int  k,  int   startIndex) {
   6           if  (path.size() ==  k) {
   7               result.push_back(path);
   8               return  ;
   9           }
  10           for  ( int  i = startIndex; i <= n - (k - path.size()) +  1 ; i++) {  //   优化的地方 
 11              path.push_back(i);  //   处理节点 
 12              backtracking(n, k, i +  1  );
  13              path.pop_back();  //   回溯,撤销处理的节点 
 14           }
  15       }
  16   public  :
  17  
 18      vector<vector< int >> combine( int  n,  int   k) {
  19          backtracking(n, k,  1  );
  20           return   result;
  21       }
  22  }; 

 

 

查看更多关于代码随想录算法训练营第二十四天| 理论基础 77. 组合的详细内容...

  阅读:37次