LeetCode77. 组合

题目

给定两个整数 n 和 k,返回 1 ... 中所有可能的 k 个数的组合。

分析

本题属于回溯中的组合问题

首先回溯问题的整体模板,如下(参考代码随想的Carl)

 1 void backtracking(参数) {
 2     if (终止条件) {
 3         存放结果;
 4         return;
 5     }
 6 
 7     for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
 8         处理节点;
 9         backtracking(路径,选择列表); // 递归
10         回溯,撤销处理结果
11     }
12 }

递归函数的参数不仅要有题目中的 N 和 K,还要记录本层递归中每次递归搜索的位置。每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围,就是要靠startIndex

 

 相当于对原本的搜索树修剪,越搜索树宽度越窄,因为有些结果在之前就搜索过了。

代码

class Solution {
public:
    vector<int>path;//存放单条路径的结果
    vector<vector<int>>res; //存放路径结果的集合

    void backtracking(int n,int k,int startIndex){
        //终止条件
        if(path.size() == k){
            res.push_back(path);//存放结果
            return;
        }
        //for循环对搜索树横向遍历,递归对搜索树纵向遍历
        //for循环的开始搜索位置是strartIndex
        for(int i = startIndex;i <= n;i++){
            path.push_back(i);//对节点进行处理
            backtracking(n,k,i+1);//递归
            path.pop_back();//回溯,撤销对节点的处理
        }
    }
    vector<vector<int>> combine(int n, int k) {
        backtracking(n,k,1);
        return res;
    }
};

其实这样并不算最优,因为还可以将搜索树进行剪枝

 

 for 循环选择的起始位置之后的元素个数 已经不足我们需要的元素个数,就没有必要搜索,直接剪掉。

所以优化后的代码如下:

 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 };

 

posted @ 2021-01-28 11:49  Uitachi  阅读(52)  评论(0编辑  收藏  举报