算法题每日一练---第88天:组合总和 II

1,766 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第23天,点击查看活动详情

一、问题描述

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意: 解集不能包含重复的组合。

题目链接:组合总和 II

二、题目要求

样例

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

考察

1.回溯算法
2.建议用时20~35min

三、问题分析

本题是开启回溯算法刷题的第2题,之前没有了解过可以看一下这篇题解,讲解比较详细。

算法题每日一练---第85天:组合

对于这一题最重要的步骤就是判重,因为[1 1 6][1 6 1]代表相同的结果。

遍历的话和算法题每日一练---第85天:组合原理是一样的。

3.png

第一式:函数初始

我们要初始化一个函数来承载回溯,函数里面的参数如何确定呢?

首先,要传入给定的数组信息,目标值,初始遍历的下标。

    vector<int>t;
    vector<vector<int>> v;
void DFS(int begin,int target,vector<int>& candidates)//函数初始

第二式:终止条件

什么时候结束继续向下开始搜索的条件呢?

题目要求target可以由数组的哪些元素组成,每加入一个元素,target要减去这个元素的值。

如果target==0,那么就符合终止条件。

if(target==0)//终止条件
{
    v.push_back(t);
    return;
}

第三式:剪枝优化

这一题剪枝比较简单,上面终止的条件不是等于0咩。

那如果target已经小于0,那我们还有必要继续搜索吗,完全没必要。

if(target<0)//剪枝
      return;

第四式:递归处理

for(i=begin;i<candidates.size();i++)//递归处理
{
     if(i>begin && candidates[i]==candidates[i-1])//判重
        continue;
      if(candidates[i]<=target)
      {
           t.push_back(candidates[i]);
           DFS(i+1,target-candidates[i],candidates);
           t.pop_back();
      }
}

这一题最难搞懂的是判重是如何实现的,首先我们将图中的元素进行排序处理,结果为:

[1,2,2,2,5]

那么相同的元素就会相邻,如上图最左侧的分支,-2会进行三个操作,但三个操作我们只需要一个,另外的两个分支都是相同的遍历。所以,这里我们也要进行剪枝操作。

那如何剪枝?

在循环遍历里面执行,只要在begin遍历之后的元素值和前一个值相同,那么就跳过,不进行操作。

模板

void DFS(变量)//函数初始
{
    if(条件1||条件2...)//终止条件
    {
         v.push_back(t);
         return;
    }
    if (条件1||条件2...)//剪枝
         return;
    for(int i=cur;i<=n;i++)//递归处理
    {
        //选择当前数字
        DFS(向下遍历);
        //回退
    } 
}

冲冲冲!

25.gif

四、编码实现

class Solution {
public:
    vector<int>t;
    vector<vector<int>> v;
    void DFS(int begin,int target,vector<int>& candidates)//函数初始
    {
        int i;
        if(target==0)//终止条件
        {
            v.push_back(t);
            return;
        }
        if(target<0)//剪枝优化
            return;
        for(i=begin;i<candidates.size();i++)//递归处理
        {
            if(i>begin && candidates[i]==candidates[i-1])//判重
                continue;
            if(candidates[i]<=target)
            {
                t.push_back(candidates[i]);
                DFS(i+1,target-candidates[i],candidates);
                t.pop_back();
            }
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(),candidates.end());//排序
        DFS(0,target,candidates);
        return v;
    }
};

五、测试结果

1.png

2.png