回溯算法 组合问题(一)

题目来自leetcode 77题

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

你可以按 任何顺序 返回答案。

示例 1:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

示例 2:

输入:n = 1, k = 1
输出:[[1]]

组合问题的思考

回溯算法的步骤:确定返回值与参数-确定递归函数-确定单层递归-回溯(关键)

确定返回值与参数

首先设定两个全局变量,result 和 path:

vector<vector<int>> result;  
vector<int> path; //result是存储所有path组合的,path存储每一次取出的组合;

然后将条件中要用到的输入n值与k值,和另外再假设一个值startIndex,记录取出的一个数值,作为参数。

void backtracking(int n,int k,int startIndex);

递归函数与终止条件

void backtracking(int n ,int k ,int startIndex)

把这次回溯当作是一棵树,那么访问到叶子节点,也就是当path的大小刚好是k值,那么就可以结束一次取值;

if(path.size() == k) {
    result.push_back(path);
    return;}

单层循环

递归函数适用于纵向,往下遍历,for循环适用于横向循环;

for(int i = startIndex;i <=n ;i++) {
 path.push_back(i);//处理节点
 backtracking(n,k,i+1);//往下遍历
 path.pop_back();//回溯,撤销处理
 }

全部代码

class Solution {
   private:
      vector<vector<int>> result;  
      vector<int> path;
      void backtracking (int n,int k,int startIndex)
      {
        if(path.size() == k) { //终止条件
            result.push_back(path);
            return;}

        for(int i = startIndex;i <=n;i++)
        {
            path.push_back(i);//处理节点
            backtracking(n,k,i+1);//递归
            path.pop_back();//回溯,撤销处理的节点
        }
      }
    public:
      vector<vector<int>> combine(int n,int k)
      {
        result.clear();
        path.clear();
        backtracking(n,k,1);
        return result;
      }
};

剪枝优化

个人觉得,回溯算法上的剪枝优化,主要还是针对横向遍历时候的条件范围。
就是对于变量i在for循环中选择的起始位置:

for (int i = startIndex; i <= n; i++)
  • 已经选择的元素个数:path.size()
  • 还需要的元素个数:k-path.size()
  • 在集合n中至多要从该起始位置(n-(k-path.size()+1))开始遍历

所有优化之后的代码主要如下:

for(int i = startIndex; i <= n - (k-path.size()+1);i++)

小结

组合问题当中,使用回溯算法三部曲,其实也是递归三步法,只不过在递归的过程中,多了回溯的一步;
同时要注意,对于题目中的剪枝优化,主要着手的地方就是横向遍历的条件。

posted @ 2022-10-10 23:00  JaydenHuan  阅读(30)  评论(0编辑  收藏  举报