递归&回溯-子集之和
时间: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;
}
结果
瞅啥瞅,没了,动动手码起来吧
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- JS日期格式化转换方法
- lerna 包管理器
- 解决React前端在开发环境的跨域问题
- Java垃圾回收之jconsole分析
- Spring Boot 系列:日志动态配置详解
- Js电子时钟
- 函数的定义和调用
- this指向
- 严格模式
- 闭包与高阶函数
- IDEA设置maven修改settings.xml配置文件无法加载仓库
- 在Linux下如何根据域名自签发OpenSSL证书与常用证书转换 修改openssl.cnf配置文件创建根证书自签发泛域名证书将crt转pem格式生成 p12 格式的
- CAS 原子操作
- FlutterDojo设计之道—状态管理之路(七)
- Kubernetes K8S之存储ConfigMap详解 通过目录创建通过文件创建通过命令行创建通过yaml文件创建当前存在的ConfigMap使用ConfigMap