递归&回溯-子集之和

时间:2022-06-25
本文章向大家介绍递归&回溯-子集之和,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一 唠嗑

今天有两篇文章!怎么样,惊不惊喜意不意外

今天这两道题昨晚,面试时候子集问题你被问住了,请来找我

二 上题!

面试官问完你上个题,你给出了,他说,那我改一下

Q:已知一个数组,可能有重复元素,给定值target,求出所有子集中,和为target的,不重复的子集。

举例:对于数组【10, 1, 2, 7, 6, 1, 5】,target = 8

则需要返回【【1,7】,【1,2,5】,【2,6】,【1,1,6】】

三 冷静分析

此时你脱口而出:这没区别啊,上道题代码原封不动,我算一下result的和是不是target,是的话,再追加进新数组,不就行了,这种题也拿来面我,渣渣

但你知道,这肯定不是他想要的答案,对吗

那能不能优化呢?

冷静分析:

这道题多了什么?target!

我们在递归&回溯过程中用到了吗?没有!

怎么用?

不卖关子,在深搜算法题中,用的非常广泛的思想-剪枝

当有额外条件时,如果不加以限制,那么将多出很多,不满足额外条件的搜索,没有意义。所以这时候我们要利用条件,在递归回溯时判断,进而决定走向再执行的过程,我们成为剪枝。

那么就很简单了,设置变量sum,当当前sum值>target时,比如10 > 8,根本不需要往下继续递归了,这就降低了时间复杂度,在数据量很大的情况下,剪枝的好处一目了然。

当然,记得回溯时,变量sum也要对应地减去当前元素。

四 完整代码及注释

//
// Created by renyi on 2019/6/26.
//
#include <iostream>
#include <set>
#include <vector>
#include <algorithm>
using namespace std;

void findSubsetsSum(int i, vector<int> &nums, vector<int> &item, vector<vector<int>> &result, set<vector<int>> &res_set, int sum, int target){
    if (i >= nums.size() || sum > target){//sum是当前item中元素的和,一旦大于了target,比如10 > 8,就不在递归回溯了,即,剪枝
        return;
    }

    sum += nums[i];//累加sum
    item.push_back(nums[i]);//追加进临时数组item

    if (res_set.find(item) == res_set.end() && sum == target){//item没在集合中,且sum==target,即找到了和是target的子集
        result.push_back(item);//追加结果和集合
        res_set.insert(item);
    }
    findSubsetsSum(i + 1, nums, item, result, res_set, sum, target);
    sum -= nums[i];//回溯时,sum要减去nums【i】并从item中删除nums【i】
    item.pop_back();
    findSubsetsSum(i + 1, nums, item, result, res_set, sum, target);
}

vector<vector<int>> subsets(vector<int> &nums, int target){
    vector<vector<int>> result;
    vector<int> item;
    set<vector<int>> res_set;
    sort(nums.begin(), nums.end());//和上道题一样,依然先排序
    findSubsetsSum(0, nums, item, result, res_set, 0, target);
    return result;
}

int main(){
    vector<int> nums;
    nums.push_back(10);
    nums.push_back(1);
    nums.push_back(2);
    nums.push_back(7);
    nums.push_back(6);
    nums.push_back(1);
    nums.push_back(5);
    vector<vector<int>> result;
    result = subsets(nums, 8);
    for (int i = 0; i < result.size(); i++) {
        if (result[i].size() == 0){
            printf("[]");
        }
        for (int j = 0; j < result[i].size(); j++) {
            printf("[%d]", result[i][j]);
        }
        printf("n");
    }
    return 0;
}

结果

瞅啥瞅,没了,动动手码起来吧