Leetcode 剑指 Offer II 082.组合总和 II

题目难度: 中等

原题链接

今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

给定一个可能有重复数字的整数数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次,解集不能包含重复的组合。

示例 1:

  • 输入: candidates = [10,1,2,7,6,1,5], target = 8,
  • 输出:
[
    [1,1,6],
    [1,2,5],
    [1,7],
    [2,6]
]

示例 2:

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

提示:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

题目思考

  1. 如何做到不添加重复组合?
  2. 如果限制只能用递归或者迭代, 如何解决?

解决方案

方案 1

思路
  • 分析题目, 不难发现这道题和上一道题(剑指 Offer II 081.组合总和)非常类似, 区别只是数字存在重复, 且只能取一次
  • 但如果我们仍采用之前的两分支的做法, 即选取当前数字和不选当前数字, 则会出现重复组合
  • 举个例子, 假设 candidates 是[1,2,1], 而 target 是 3, 那么只选第一个 1 对应的组合是[1,2], 而只选第二个 1 对应的组合是[2,1], 这就导致重复了, 如何解决呢?
  • 既然在不同位置选择相同的数字会导致重复, 那么我们可以将相同数字聚合起来, 对应的就是计数字典{key:cnt}
  • 然后针对字典的每个 key, 我们都可以有 0,1,…,cnt 种选择, 对应就是组合里不添加该数字, 添加一个,…,添加 cnt 个
  • 然后其余部分就和上一道题非常类似了, 本质上来说, 就是相当于将上一道题的无限制添加某个数字改成最多添加 cnt 次
  • 我们可以基于上述分析进行递归求解, 具体做法如下:
    • 首先将原始数组转换成计数字典, 并得到 key 的列表
    • 传入当前下标, 当前组合, 以及当前组合的数字之和
    • 如果当前数字和恰好等于 target, 则将当前组合加入最终结果, 并返回
    • 如果当前数字和已经大于 target, 或者当前下标超出 key 列表的范围, 则没必要继续递归了, 直接返回
    • 如果不是上述两种情况, 则说明可以继续递归, 此时可以使用 0~cnt 个数字, 共有 cnt+1 种情况
    • 对于每种情况, 将对应数目的当前数字添加到当前组合并更新数字和, 然后下标加 1
  • 由于每条递归路径添加的数字个数都不一样, 所以每个递归出口形成的有效组合也各不相同, 无需手动去重
复杂度
  • 时间复杂度 O(2^N): 假设原始数组共有 N 个数字, 每个数字都要么添加到组合, 要么不添加, 所以最多判断 2^N 种组合, 总时间是 2^N
  • 空间复杂度 O(N): 计数字典和 key 列表需要存储最多 N 个元素, 而递归栈在最差情况下长度同样是 N
代码
Python 3
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        # 方法1: 转计数字典+多分支递归
        res = []
        # 将原始数组转成计数字典, 并记录key列表
        cnts = collections.Counter(candidates)
        keys = list(cnts.keys())

        # 递归传入当前下标, 当前组合以及当前组合的数字之和
        def dfs(i, path, sm):
            if sm == target:
                # 递归出口#1, 找到一个有效组合, 将其加入最终结果集
                res.append(path)
                return
            if i >= len(keys) or sm > target:
                # 递归出口#2, 当前组合已经不可能满足要求了, 直接返回
                return
            for cnt in range(cnts[keys[i]] + 1):
                # 针对0~cnt, 将对应次数的数字加入组合并更新数字和, 然后继续处理下一个key
                dfs(i + 1, path + [keys[i]] * cnt, sm + cnt * keys[i])

        dfs(0, [], 0)
        return res

方案 2

思路
  • 接下来我们尝试用迭代的思路来解决
  • 我们可以将递归函数的三个参数作为一个三元组存储起来
  • 然后遍历这个三元组列表, 同样利用递归方案的分析来进行相应处理
  • 只是需要将递归出口的 return 改成 continue, 以及将递归调用改成追加新的三元组到列表中
  • 下面代码中有详细的注释, 方便大家理解
复杂度
  • 时间复杂度 O(2^N): 分析同方案 1
  • 空间复杂度 O(2^N): 需要保存所有可能的三元组
代码
Python 3
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        # 方法2: 转计数字典+三元组迭代
        res = []
        # 将原始数组转成计数字典, 并记录key列表
        cnts = collections.Counter(candidates)
        keys = list(cnts.keys())
        # (当前下标,当前组合,当前组合的数字之和)
        tuples = [(0, [], 0)]
        for i, path, sm in tuples:
            if sm == target:
                # 找到一个有效组合, 将其加入最终结果集, 继续循环
                res.append(path)
                continue
            if i >= len(keys) or sm > target:
                # 当前组合已经不可能满足要求了, 不再继续处理它, 继续循环
                continue
            for cnt in range(cnts[keys[i]] + 1):
                # 针对0~cnt, 将对应次数的数字加入组合并更新数字和, 然后继续处理下一个key
                tuples.append((i + 1, path + [keys[i]] * cnt, sm + cnt * keys[i]))
        return res

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot【2】集成 MyBatis Plus

SpringBoot 集成 MyBatis Plus 前言修改 pom.xml修改配置文件添加 实体类添加 持久层接口添加 持久层 XxxMapper.xml 文件添加 业务接口层添加 业务接口实现类添加 控制层添加 MyBatis 配置AutoFillMetaObjectHandlerMyBatisPlusConfig 验证 前言 由于 MySQL 备份/恢复测试&am…

哪里有海量的短视频素材,以及短视频制作教程?

在当下&#xff0c;短视频已成为最火爆的内容形式之一&#xff0c;尤其是在抖音上。但很多创作者都面临一个问题&#xff1a;视频素材从哪里来&#xff1f;怎么拍摄才能吸引更多观众&#xff1f;别担心&#xff0c;今天我将为大家推荐几个宝藏网站&#xff0c;确保你素材多到用…

CANoe连接Option Scope使用方法

系列文章目录 文章目录 系列文章目录前言一、前提条件二、CANoe配置三、PicoScope接线四、CANoe捕捉报文五、眼图功能前言 本文档主要介绍如何使用CANoe Option .Scope捕获CAN总线上的物理波形,并利用眼图进行分析。 一、前提条件 使用CANoe Option .Scope,需要具备以下条件…

时间复杂度与空间复杂度题目讲解

前言&#xff1a; 在前面我们了解到了时间复杂度与空间复杂度&#xff0c;这里我们就可以尝试一下做一些关于时间复杂度与空间复杂度的题目。 1. 数组篇 题目一&#xff1a;消失的数字 消失的数字&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 看…

基于python-CNN卷积网络训练识别牛油果和猕猴桃-含数据集+pyqt界面

代码下载地址&#xff1a; https://download.csdn.net/download/qq_34904125/89383066 本代码是基于python pytorch环境安装的。 下载本代码后&#xff0c;有个requirement.txt文本&#xff0c;里面介绍了如何安装环境&#xff0c;环境需要自行配置。 或可直接参考下面博文…

.net8 blazor auto模式很爽(四)改造vs自动创建的Blazor auto为前后端分离模式(3)

BlazorApp1的appsettings改为下面的内容,注意 "BaseAddress": "https://localhost:7228"这个商端口号要和Properties的launchSettings内容一致&#xff1a; {"BaseAddress": "https://localhost:7228","Logging": {"L…

花卉识别-python-pytorch-CNN深度学习含数据集+pyqt界面

代码下载地址&#xff1a; https://download.csdn.net/download/qq_34904125/89383063 本代码是基于python pytorch环境安装的。 下载本代码后&#xff0c;有个requirement.txt文本&#xff0c;里面介绍了如何安装环境&#xff0c;环境需要自行配置。 或可直接参考下面博文…

Matlab使用Simulink仿真实现AM和BPSK信号的解调

前言 本篇实现了基于AM和BPSK调制的通信系统&#xff0c;采用Bernoulli Binary Generator生成随机二元序列&#xff0c;码元速率为0.5秒/个。AM调制使用Sine Wave模块生成载波&#xff0c;频率40Hz&#xff0c;相位π/2。BPSK调制通过Switch模块切换相位0和π的载波。信号传输…

sap怎么批量给信息记录打上删除标识

1.MEMASSIN-----事务代码 2.选择完成字段 3.根据条件查询需要冻结的信息记录 4.输入查询条件 5.全部勾选完成标识&#xff0c;点击保存&#xff0c;即可冻结完成

Spark groupByKey和reduceByKey对比

在 Apache Spark 中&#xff0c;groupByKey 和 reduceByKey 都是用于对键值对 (key-value) 数据集进行分组和聚合的操作。然而&#xff0c;它们在性能和使用场景上有显著的差异。 groupByKey 函数 groupByKey 将数据集中的所有键相同的值进行分组&#xff0c;然后返回一个键值…

Radis初阶 Radis基本命令与在Springboot中访问Radis

阿里网盘链接 文章目录 初始NoSQL数据库对比MySQL数据库从结构方面&#xff1a;从关系方面&#xff1a;从查询方式&#xff1a;从事物方面&#xff1a; Redis入门Redis数据结构访问Radis通用命令&#xff08;tab键&#xff1a;自动补全&#xff09;KEYSDELEXISTSEXPIRETTL Str…

【TB作品】MSP430G2553,DS1302,LCD1602,时间读取和显示,万年历,Proteus仿真

效果 部分代码 #include <MSP430.h> #include "ds1302.h" #include "LCD.h"//关掉ccs优化&#xff0c;并且Convert_BCD_To_Dec函数中只能是10.0f才行&#xff0c;不然有bugvoid main(void) {char cnt 0;char disp[16];WDTCTL WDTPW WDTHOLD; /* …

基于51单片机的智能水表

一.硬件方案 本设计主要以51单片机作为主控处理器的智能水表&#xff0c;该水表能够记录总的用水量和单次用水量&#xff0c;当用水量超出设定值时系统发出声光报警提醒&#xff0c;水量报警值能够通过按键进行自行设置&#xff0c;并且存储于AT24C02中&#xff0c;并且可以测…

【Ardiuno】使用ESP32单片机网络功能调用API接口(图文)

接着上文连通wifi后&#xff0c;我们通过使用HTTPClient库进行网络相关操作&#xff0c;这里我们通过http协议进行接口调用。 为了简化操作&#xff0c;小飞鱼这里使用了本地服务器上的文件作为接口&#xff0c;正常操作时会调用接口后&#xff0c;将服务器返回的数据进行解析…

Vue32-挂载流程

一、init阶段 生命周期本质是函数。 1-1、beforeCreate函数 注意&#xff1a; 此时vue没有_data&#xff0c;即&#xff1a;data中的数据没有收到。 1-2、create函数 二、生成虚拟DOM阶段 注意&#xff1a; 因为没有template选项&#xff0c;所以&#xff0c;整个div root都…

stable diffusion最全插件大全,新手必备指南

Stable diffusion30个必备插件推荐&#xff0c;给我点个赞吧&#xff0c;兄弟们 1&#xff0c;ComfyUI&#xff0c;SD扩展里面直接搜索就行&#xff0c; ComfyUI 是一个基于节点操作的UI界面&#xff0c;玩过建模的更容易学 安装后大概是这样的 评价&#xff1a;comfyui,更适…

换卡槽=停机?新手机号使用指南!

刚办理的手机号莫名其妙的就被停用了&#xff1f;这到底是怎么回事&#xff1f;这篇文章快来学习一下吧。 ​ 先说一下&#xff0c;你的手机为什么被停机&#xff1f; 现在运营商对于手机卡的使用有着非常严格的要求&#xff0c;尤其是刚办理的新号码&#xff0c;更是“严上加…

神经网络学习1—nn.Module

nn.module 为所有神经网络提供了一个模板 import torch.nn as nn import torch.nn.functional as Fclass Model(nn.Module):def __init__(self):super(Model, self).__init__()self.conv1 nn.Conv2d(1, 20, 5)self.conv2 nn.Conv2d(20, 20, 5)def forward(self, x):x F.rel…

解决Echarts图表中tooltip无法换行问题

解决Echarts图表中tooltip无法换行问题 这里设置宽度、颜色都是是可以生效的&#xff0c;但就是不换行 解决办法tooltip. extraCssText extraCssText: max-width:300px; white-space:pre-wraptooltip: { // 单个柱子显示悬浮内容extraCssText: max-width:300px; white-space…

工业网关在智能制造中的具体应用和效果-天拓四方

随着工业4.0时代的到来&#xff0c;智能制造正逐渐成为工业领域的发展趋势。作为连接物理世界与数字世界的桥梁&#xff0c;工业网关在智能制造中发挥着至关重要的作用。本案例将详细阐述工业网关在某一制造企业中的具体应用&#xff0c;展示其如何助力企业实现数字化转型&…