leetcode 77. 组合

2023.7.17

leetcode 77. 组合_第1张图片

         今天正式开始回溯系列,这是一道经典回溯题。 先上一个经典回溯模板:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

         本题在模板的基础上,还要加上一个参数start,用于防止在集合中选到同样的元素,如[1,1][2,2]等。 下面细节看代码:

class Solution {
public:
    vector> ans;
    vector path;
    void backtracking(int n, int k, int start)
    {
        if(path.size() == k) 
        {
            ans.push_back(path);
            return;
        }
        for(int i=start; i<=n; i++)
        {
            path.push_back(i);
            backtracking(n,k,i+1);
            path.pop_back();
        }
    }
    vector> combine(int n, int k) 
    {
        backtracking(n,k,1);
        return ans;
    }
};

        剪枝优化:

        考虑一种极端情况:当n和k都取4时,那么当{1,2,3,4}添加到ans之后,从元素2开始遍历就没有意义了,因为需要4个元素,而从2开始遍历最多只有3个元素。 所以语句 for(int i=start; i<=n; i++)可以做一个优化:

  • 我们需要的元素个数为:k-path.size()   
  • 集合剩余元素为:n-i+1 (包括初始元素i,所以要+1)
  • 我们需要的元素应该小于等于集合剩余元素:k-path.size()   <=  n-i+1,反解得到:                    i <= n-k+path.size()+1

优化后的代码为:

class Solution {
public:
    vector> ans;
    vector path;
    void backtracking(int n, int k, int start)
    {
        //中止条件
        if(path.size() == k) 
        {
            ans.push_back(path);
            return;
        }
        //for(int i=start; i<=n; i++)
        for(int i=start; i<=n-k+path.size()+1; i++)
        {
            path.push_back(i);
            backtracking(n,k,i+1);
            path.pop_back();
        }
    }
    vector> combine(int n, int k) 
    {
        backtracking(n,k,1);
        return ans;
    }
};

你可能感兴趣的:(leetcode专栏,leetcode,算法,职场和发展,c++,数据结构)