LeetCode 39. Combination Sum【回溯,剪枝】中等

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。



给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = `[2,3,6,7],` target = `7`
输出:[[2,2,3],[7]]
解释:
23 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5]`,` target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

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

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

解法 搜索回溯

对于这类寻找所有可行解的题,我们都可以尝试用「搜索回溯」的方法来解决。

回到本题,我们定义递归函数 d f s ( t a r g e t , c o m b i n e , i d x ) dfs(target, combine, idx) dfs(target,combine,idx) 表示当前在 c a n d i d a t e s candidates candidates 数组的第 i d x idx idx 位,还剩 t a r g e t target target 要组合,已经组合的列表为 c o m b i n e combine combine 。递归的终止条件为 t a r g e t ≤ 0 target\le 0 target0 或者 c a n d i d a t e s candidates candidates 数组被全部用完。

那么在当前的函数中,每次我们可以选择跳过不用第 i d x idx idx 个数,即执行
d f s ( t a r g e t , c o m b i n e , i d x + 1 ) dfs(target,combine,idx+1) dfs(target,combine,idx+1) 。也可以选择使用第 i d x idx idx 个数,即执行 d f s ( t a r g e t − c a n d i d a t e s [ i d x ] , c o m b i n e , i d x ) dfs(target−candidates[idx],combine,idx) dfs(targetcandidates[idx],combine,idx) ,注意到**每个数字可以被无限制重复选取,因此搜索的下标仍为 i d x idx idx **。

更形象化地说,如果我们将整个搜索过程用一个树来表达,即如下图呈现,每次的搜索都会延伸出两个分叉,直到递归的终止条件,这样我们就能不重复且不遗漏地找到所有可行解:

当然,搜索回溯的过程一定存在一些优秀的剪枝方法来使得程序运行得更快,而这里只给出了最朴素不含剪枝的写法

class Solution {
public:vector<vector<int>> ans;void dfs(vector<int>& candidates, int target,  vector<int>& combine, int idx) {if (idx == candidates.size()) return;if (target == 0) {ans.emplace_back(combine);return;}// 直接跳过dfs(candidates, target, combine, idx + 1);// 选择当前数if (target - candidates[idx] >= 0) {combine.emplace_back(candidates[idx]);dfs(candidates, target - candidates[idx], combine, idx);combine.pop_back();}}vector<vector<int>> combinationSum(vector<int>& candidates, int target) {vector<int> combine;dfs(candidates, target, combine, 0);return ans;}
};

还可以进行排序,然后剪枝:

class Solution {
public:vector<vector<int>> ans;void dfs(vector<int>& candidates, int target,  vector<int>& combine, int idx) { if (idx == candidates.size()) return;if (target == 0) {ans.emplace_back(combine);return;}if (target - candidates[idx] < 0) return;// 直接跳过dfs(candidates, target, combine, idx + 1);// 选择当前数combine.emplace_back(candidates[idx]);dfs(candidates, target - candidates[idx], combine, idx);combine.pop_back();}vector<vector<int>> combinationSum(vector<int>& candidates, int target) {vector<int> combine;sort(candidates.begin(), candidates.end());dfs(candidates, target, combine, 0);return ans;}
};

这种不在意组合内元素顺序的,比如 [2,2,3][3,2,2] 都是答案的,反而要按照特定的某种顺序进行搜索,保证一个答案只会搜索出特定的一种顺序。
在意排列或组合内元素顺序的,往往只需要用哈希表来记录一个元素之前是否使用过。

复杂度分析:

  • 时间复杂度: O ( S ) O(S) O(S) ,其中 S S S 为所有可行解的长度之和。从分析给出的搜索树,我们可以看出时间复杂度取决于搜索树所有叶子节点的深度之和,即所有可行解的长度之和。在这题中,我们很难给出一个比较紧的上界,我们知道 O ( n × 2 n ) O(n \times 2^n) O(n×2n) 是一个比较松的上界,即在这份代码中, n n n 个位置每次考虑选或者不选,如果符合条件,就加入答案的时间代价。但实际运行的时候,因为不可能所有的解都满足条件,递归时我们还会用 t a r g e t − c a n d i d a t e s [ i d x ] ≥ 0 target−candidates[idx]≥0 targetcandidates[idx]0 进行剪枝,所以实际运行情况是远远小于这个上界的。
  • 空间复杂度: O ( t a r g e t ) O(target) O(target) 。除答案数组外,空间复杂度取决于递归的栈深度,在最差情况下需要递归 O ( t a r g e t ) O(target) O(target) 层。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/131822.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cudnn-windows-x86_64-8.6.0.163_cuda11-archive 下载

网址不太好访问的话,请从下面我提供的分享下载 Download cuDNN v8.6.0 (October 3rd, 2022), for CUDA 11.x 此资源适配 cuda11.x 将bin和include文件夹里的文件&#xff0c;分别复制到C盘安装CUDA目录的对应文件夹里 安装cuda时自动设置了 CUDA_PATH_V11_8 及path C:\Progra…

数据结构——排序算法——快速排序

快速排序算法的基本思想是 1.从数组中取出一个数&#xff0c;称之为基数&#xff08;pivot&#xff09; 2.遍历数组&#xff0c;将比基数大的数字放到它的右边&#xff0c;比基数小的数字放到它的左边。遍历完成后&#xff0c;数组被分成了左右两个区域 3.将左右两个区域视为两…

leecode 每日一题 2596. 检查骑士巡视方案

2596. 检查骑士巡视方案 骑士在一张 n x n 的棋盘上巡视。在 有效 的巡视方案中&#xff0c;骑士会从棋盘的 左上角 出发&#xff0c;并且访问棋盘上的每个格子 恰好一次 。 给你一个 n x n 的整数矩阵 grid &#xff0c;由范围 [0, n * n - 1] 内的不同整数组成&#xff0c;其…

记录selenium和chrome使用socks代理打开网页以及查看selenium的版本

使用前&#xff0c;首先打开socks5全局代理。 之前我还写过一篇关于编程中使用到代理的情况&#xff1a; 记录一下python编程中需要使用代理的解决方法_python 使用全局代理_小小爬虾的博客-CSDN博客 在本文中&#xff0c;首先安装selenium和安装chrome浏览器。 参考我的文章…

vue中实现瀑布流布局

父组件 <template><WaterfallFlow :list"list"/> </template><script setup lang"ts">import WaterfallFlow from "/components/WaterfallFlow.vue"; import {reactive} from "vue"; type listType {height…

向量范数及其Python代码

【向量范数】 向量由于既有大小又有方向&#xff0c;所以不能直接比较大小。 向量范数通过将向量转化为实数&#xff0c;然后进行向量的大小比较。 所以&#xff0c;向量范数是用于度量“向量大小”的量。 设向量 &#xff0c;则有&#xff1a; ● 向量的 范数&#xff1a; ●…

C语言入门Day_19 初识函数

目录 1.函数的定义 2.函数的调用 3.易错点 4.思维导图 前言&#xff1a; printf()我们已经很熟悉了&#xff0c;它有一个特定的功能&#xff0c;就是在屏幕上输出一行文字。之前的课程我们都称呼printf()为一个功能&#xff0c;实际上ta在编程中有个特定的名字——函数。 …

嵌入式学习笔记(28)按键和CPU的中断系统

按键的物理特性 (1)、平时没人按的时候&#xff0c;弹簧把按键按钮弹开。此时内部断开的。 (2)、有人按下的时候&#xff0c;手的力量克服弹簧的弹力&#xff0c;将按钮按下&#xff0c;此时内部保持接通&#xff08;闭合&#xff09;状态&#xff1b;如果手拿开&#xff0c;…

VSCode 安装使用教程 环境安装配置 保姆级教程

一个好用的 IDE 不仅能提升我们的开发效率&#xff0c;还能让我们保持愉悦的心情&#xff0c;这样才是非常 Nice 的状态 ^_^ 那么&#xff0c;什么是 IDE 呢 &#xff1f; what IDE&#xff08;Integrated Development Environment&#xff0c;集成开发环境&#xff09;是含代码…

线性代数的本质(十)——矩阵分解

文章目录 矩阵分解LU分解QR分解特征值分解奇异值分解奇异值分解矩阵的基本子空间奇异值分解的性质矩阵的外积展开式 矩阵分解 矩阵的因式分解是把矩阵表示为多个矩阵的乘积&#xff0c;这种结构更便于理解和计算。 LU分解 设 A A A 是 m n m\times n mn 矩阵&#xff0c;…

论文阅读 - Outlier detection in social networks leveraging community structure

目录 摘要 1. Introduction 2. Related works 3. Preliminaries 3.1. 模块化度量 3.2. Classes of outliers 3.2.1. 点异常 3.2.2. Contextual anomalies 3.2.3. Collective anomalies 3.3. Problem definition 3.4. Outliers score 4. Methodology 4.1. Proposed appr…

86 # express 基本实现

koa 和 express 的区别 koa 内部原理使用 es6 来编写的&#xff08;promise async await&#xff09;&#xff0c;express 是使用 es5 来编写的&#xff0c;内部是基于回调函数来实现express 内置了很多中间件&#xff08;功能会比 koa 强大一些&#xff0c;内部集成了路由&a…

OPENCV--实现meanshift图像分割

Meanshift原理 效果图 API # -*- coding:utf-8 -*- """ 作者:794919561 日期:2023/9/13 """ import cv2 import numpy as npimg = cv2.imread("F:\\learnOpenCV\\openCVLearning\\pictures\\Lena.jpg

过拟合、欠拟合、泛化误差、训练误差

模型容量的影响&#xff1a; 泛化误差&#xff1a; 当训练的模型的容量过了最优点时&#xff0c;泛化误差反而升高&#xff0c;这是由于模型过于关注细节导致&#xff0c;模型也同时记住噪声&#xff1b;当拿来一个真的数据时&#xff0c;模型会被一些无关紧要的细节所干扰。 …

ASP.NET dotnet 3.5 实验室信息管理系统LIMS源码

技术架构&#xff1a;ASP.NET dotnet 3.5 LIMS作为一个信息管理系统&#xff0c;它有着和ERP、MIS之类管理软件的共性&#xff0c;如它是通过现代管理模式与计算机管理信息系统支持企业或单位合理、系统地管理经营与生产&#xff0c;最大限度地发挥现有设备、资源、人、技术的…

27.EI文章复现《高比例清洁能源接入下计及需求响应的配电网重构》

下载地址&#xff1a;高比例清洁能源接入下计及需求响应的配电网重构 1主要内容 该程序复现《高比例清洁能源接入下计及需求响应的配电网重构》&#xff0c;以考虑网损成本、弃风弃光成本和开关操作惩罚成本的综合成本最小为目标&#xff0c;针对配电网重构模型的非凸性&…

PyTorch实战-实现神经网络图像分类基础Tensor最全操作详解(一)

目录 前言 一、PyTorch数据结构-Tensor 1.什么是Tensor 2.数据Tensor使用场景 3.张量形态 标量&#xff08;0D 张量&#xff09; 向量&#xff08;1D 张量&#xff09; 矩阵(2D张量) 3D 张量与高维张量 二、Tensor的创建 1. 从列表或NumPy数组创建 2. 使用特定的初始…

5.10.WebRTC接口宏

那今天呢&#xff1f;我给大家介绍一下web rtc的接口宏&#xff0c;那之所以在现成的章节中要介绍接口宏。是由于接口在调用的过程中啊&#xff0c;会发生线程的切换&#xff0c;所以把接口宏这部分知识我们放在线程这一章还算比较合适的。 那另外呢&#xff0c;我们对于接口…

分库分表---理论

目录 一、垂直切分 1、垂直分库 2、垂直分表 3、垂直切分优缺点 二、水平切分 1、水平分库 2、水平分表 3、水平切分优缺点 三、数据分片规则 1、Hash取模分表 2、数值Range分表 3、一致性Hash算法 四、分库分表带来的问题 1、分布式事务问题 2、跨节点关联查询…

IT运维:利用数据分析平台采集Windows event log数据

概述 本文将介绍如何借助Winlogbeat和Vector在鸿鹄里采集Windows event log数据&#xff0c;使技术人员能够在鸿鹄里更便捷和高效地分析Windows event log数据。 操作步骤 Winlogbeat是一个开源的日志数据采集器&#xff0c;专门用于采集Windows操作系统中的event log数据。它可…