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)returnif i >= len(keys) or sm > target:# 递归出口#2, 当前组合已经不可能满足要求了, 直接返回returnfor cnt in range(cnts[keys[i]] + 1):# 针对0~cnt, 将对应次数的数字加入组合并更新数字和, 然后继续处理下一个keydfs(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)continueif i >= len(keys) or sm > target:# 当前组合已经不可能满足要求了, 不再继续处理它, 继续循环continuefor cnt in range(cnts[keys[i]] + 1):# 针对0~cnt, 将对应次数的数字加入组合并更新数字和, 然后继续处理下一个keytuples.append((i + 1, path + [keys[i]] * cnt, sm + cnt * keys[i]))return res

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

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

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

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

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

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

相关文章

能耗监控与管理平台

在当今社会&#xff0c;随着工业化、城市化的快速发展&#xff0c;能源消耗问题日益凸显&#xff0c;节能减排已成为全社会共同关注的焦点。在这个背景下&#xff0c;一款高效、智能的能耗监控与管理平台显得尤为重要。 一、HiWoo Cloud平台的概念 HiWoo Cloud是一款集数据采…

六大维度全面焕新升级!麒麟信安服务器操作系统V3.6.1引领未来计算

昨日&#xff0c;openEuler 24.03 LTS 正式发布&#xff0c;麒麟信安作为openEuler社区重要贡献者和参与者&#xff0c;充分发挥自身在国产操作系统领域的技术优势&#xff0c;在打造安全可靠、极致体验的操作系统上与社区共同努力&#xff0c;同步推出服务器操作系统V3.6.1&am…

OpenGL3.3_C++_Windows(7)

演示 最终演示效果 ​​​​ 冯氏光照 光照原理&#xff1a;对于向量相乘默认为点乘&#xff0c;如果*lightColor(1.0f, 1.0f, 1.0f);白光&#xff0c;值不变物体的颜色显示原理&#xff1a;不被物体吸收的光反射&#xff0c;也就是由白光分解后的一部分&#xff0c;因此&…

Cask ‘oraclexxx‘ is unavailable: No Cask with this name exists.

brew search oracle-jdk或brew search --cask oracle-jdk 原因&#xff1a;Homebrew官方仓库不再维护多个旧版本的OracleJDK 不推荐使用Homebrew环境安装JDK //指定版本安装 brew install --cask temurin17 //设置 JAVA_HOME 环境变量 //找到安装的JDK 版本的路径 /usr/lib…

探索测试分享

1. “器” 项目中的实践——我们是怎么做的 本章将带你身历其境的感受到思想和方法是如何具体使用在项目里的 1.如何挖掘探索性测试的探索点&#xff0c;在任何阶段都可以利用探索测试策略找到可探索的点&#xff0c;发现产品中的bug&#xff0c;或明显或隐含。 “器”的应用…

利用74HC165实现8路并行输入口的扩展

代码&#xff1a; #include <mega16.h>// Declare your global variables here #define hc165_clk PORTB.0 #define hc165_lp PORTB.1 #define hc165_out PINB.2unsigned char read_hc165(void) {unsigned char data0,i,temp0x80;hc165_lp0;hc165_lp1; for(i0;i<7;i)…

汇编:内联汇编和混合编程

C/C内联汇编 C/C 内联汇编&#xff08;Inline Assembly&#xff09;是一种在C或C代码中嵌入汇编语言指令的方法&#xff0c;以便在不离开C/C环境的情况下利用汇编语言的优势进行性能优化或执行特定的硬件操作。以下是一些详细的说明和示例&#xff0c;展示如何在C和C代码中使用…

zookeeper介绍 和 编译踩坑

zookeeper 分布式协调服务 ZooKeeper原理及介绍 - 鹿泉 - 博客园 Zookeeper是在分布式环境中应用非常广泛&#xff0c;它的优秀功能很多&#xff0c;比如分布式环境中全局命名服务&#xff0c;服务注册中心&#xff0c;全局分布式锁等等。 本项目使用其分布式服务配置中心&am…

Minecraft模组开发(fabric)之准备工作

Minecraft模组开发&#xff08;fabric&#xff09;之准备工作 最近心血来潮想开发个Minecraft的模组&#xff0c;一边学习一边开发&#xff0c;顺带着将一些步骤、学习心得整理下来。之所以选择fabric&#xff0c;是因为自己的光影包使用的是iris-fabric&#xff0c;所以就想着…

Vue41-vc实例与vm实例

一、 vc实例与vm实例的区别 vc实例与vm实例&#xff0c;99%结构都是类似的&#xff0c;仅2点不同&#xff1a; el属性data的书写格式 1-1、 el属性 vc有的功能vm都有&#xff0c;但是vm能通过el决定为哪个容器服务&#xff0c;但是vc却不行&#xff01; 1-2、data的书写格式

unity38——MemoryProfiler性能分析器,截帧分析当前性能占用率的具体文件

定义&#xff1a;性能分析器 (Unity Profiler) 是一种可以用来获取应用程序性能信息的工具。可以将性能分析器连接到网络中的设备或连接到已连接到计算机的设备&#xff0c;从而测试应用程序在目标发布平台上的运行情况。还可以在 Editor 中运行性能分析器&#xff0c;从而在开…

高精度减法的实现

这是C算法基础-基础算法专栏的第八篇文章&#xff0c;专栏详情请见此处。 引入 上次我们学习了高精度加法的实现&#xff0c;这次我们要学习高精度减法的实现。 高精度减法与高精度加法的定义、前置过程都是大致相同的&#xff0c;如果想了解具体内容&#xff0c;可以移步至我的…

显著提高iOS应用中Web页面的加载速度 - 提前下载页面的关键资源(如JavaScript、CSS和图像)

手动下载并缓存资源是一种有效的方式&#xff0c;可以确保在需要时资源已经在本地存储&#xff0c;这样可以显著提高加载速度。 缓存整个 web 页面的所有资源文件 具体实现步骤 下载和缓存资源&#xff1a;包括 HTML 文件、CSS、JavaScript 和图像。在应用启动时预加载资源。…

实现搜索功能中搜索内容高亮效果,本文通过fuzzysort库方案实现

目录 一&#xff1a;fuzzysort1.fuzzysort 介绍&#xff1a;2.需求所用方法介绍:gohighlight 3.效果实现 一&#xff1a;fuzzysort 1.fuzzysort 介绍&#xff1a; fuzzysort 是一个 JavaScript 库&#xff0c;用于对字符串数组进行模糊搜索和排序。它特别适用于自动补全&#…

Docker安装Nginx(各种错误版)

Docker安装-CSDN博客 安装启动Docker之后 docker run -d -p 81:81 --name nginx nginx 这样没有指定版本 docker run&#xff1a;启动一个新的容器。-d&#xff1a;以分离模式运行容器&#xff08;后台运行&#xff09;。-p 81:81&#xff1a;将主机的 81 端口映射到容器的 …

【网络安全学习】使用Kali做渗透情报收集-01-<域名信息主机信息>

1.收集开源情报 开源情报(Open Source Intelligence&#xff0c;OSINT)是指从各种公开的渠道中寻找和获取有价值的信息 如&#xff1a;互联网、媒体、社交网络、公共数据库等开源情报具有以下特点&#xff1a; - 丰富性&#xff1a;开源情报涵盖了各种类型和领域的信息 - 可…

Linux---系统的初步学习【项目一:Linux操作系统的安装与配置】

项目一 Linux操作系统的安装与配置 1.1 项目知识准备 1.1.1 操作系统是什么&#xff1f; ​ 操作系统&#xff08;Operating System&#xff0c;OS&#xff09;是管理计算机硬件与软件资源的计算机程序。操作系统需要处理如管理硬件、决定程序运行的优先次序、管理文件系统等…

WPF中的隧道路由和冒泡路由事件

文章目录 简介&#xff1a;一、事件最基本的用法二、理解路由事件 简介&#xff1a; WPF中使用路由事件升级了传统应用开发中的事件&#xff0c;在WPF中使用路由事件能更好的处理事件相关的逻辑&#xff0c;我们从这篇开始整理事件的用法和什么是直接路由&#xff0c;什么是冒…

WDF驱动开发-同步技术

使用自动同步 基于框架的驱动程序中几乎所有的代码都驻留在事件回调函数中。 框架会自动同步驱动程序的大部分回调函数&#xff0c;如下所示&#xff1a; 框架始终将 常规设备对象、 功能设备对象 (FDO) 和 物理设备对象 (PDO) 事件回调函数同步&#xff0c;以便每个设备一次…

Java高阶数据结构-----并查集(详解)

目录 &#x1f9d0;一.并查集的基本概念&实例&#xff1a; &#x1f92a;二.并查集代码&#xff1a; &#x1f602;三&#xff1a;并查集的一些习题&#xff1a; A.省份数量 B.等式方程的可满足性 &#x1f9d0;一.并查集的基本概念&实例&#xff1a; 并查集概念&…