当前位置: 首页 > web >正文

力扣(组合)

解析 LeetCode 77. 组合:回溯算法的高效实现

一、题目分析

在这里插入图片描述

(一)问题定义

给定整数 nk,返回 [1, n] 中所有长度为 k 的组合,组合内元素无序且不重复。

(二)核心挑战

高效枚举所有符合条件的组合,避免重复和遗漏,同时通过剪枝优化减少不必要的递归。

二、算法思路:回溯 + 剪枝

(一)回溯思想

通过递归遍历所有可能的元素选择:

  1. 选择:从当前起始位置开始,选一个数加入组合。
  2. 递归:基于当前选择,递归处理下一个位置,继续选数。
  3. 回溯:递归返回后,撤销当前选择,尝试其他数。

(二)剪枝优化

在循环中,通过条件 (n - i + 1 + answerMin.size()) >= k 剪枝:

  • n - i + 1 表示当前位置 in 还剩的可选数数量。
  • answerMin.size() 是已选数数量。
  • 若剩余可选数 + 已选数 < k,说明无法凑够 k 个数,直接跳过后续循环。

三、代码解析

class Solution {// 存储当前组合List<Integer> answerMin = new ArrayList<>(); // 存储所有符合条件的组合List<List<Integer>> result = new ArrayList<>(); public List<List<Integer>> combine(int n, int k) {// 从起始位置 1 开始回溯backtracking(n, k, 1); return result;}private void backtracking(int n, int k, int startIndex) {// 终止条件:组合长度达到 kif (answerMin.size() == k) { // 将当前组合加入结果(新建列表避免引用问题)result.add(new ArrayList<>(answerMin)); return;}// 遍历当前层可选的数,i 从 startIndex 开始for (int i = startIndex; // 剪枝:剩余数 + 已选数 >= k 才继续,否则无法凑够 k 个i <= n && (n - i + 1 + answerMin.size()) >= k; i++) { // 选择:将 i 加入当前组合answerMin.add(i); // 递归:处理下一层,起始位置为 i+1backtracking(n, k, i + 1); // 回溯:撤销选择,尝试下一个数answerMin.remove(answerMin.size() - 1); }}
}

(一)流程拆解

  1. 初始化answerMin 存当前组合,result 存所有结果。
  2. 回溯启动:调用 backtracking,从 startIndex = 1 开始。
  3. 终止条件:组合长度达 k 时,将当前组合加入 result
  4. 遍历与剪枝:循环中,通过剪枝条件跳过无法凑够 k 个数的情况;选择数 i 后递归,返回后回溯(移除 i )。
  5. 结果返回result 存储所有组合,作为最终结果。

(二)关键逻辑

  • 剪枝优化:减少不必要的递归,提升效率。例如,n=5, k=3,若已选 1 个数,当前 i=4,剩余数 5-4+1=2,已选 1,2+1=3 刚好满足 k=3,继续;若 i=5,剩余数 11+1=2 < 3,直接跳过。
  • 引用问题result.add(new ArrayList<>(answerMin)) 确保存储的是当前组合的副本,避免后续修改影响结果。

四、复杂度分析

(一)时间复杂度

  • 最坏情况(无剪枝):需枚举所有组合,数量为 C(n,k)C(n, k)C(n,k),每个组合生成时间为 O(k)O(k)O(k),总复杂度 O(k×C(n,k))O(k \times C(n, k))O(k×C(n,k))
  • 剪枝后:减少无效递归,实际复杂度低于最坏情况。

(二)空间复杂度

  • 递归栈深度最多为 kanswerMin 最多存 k 个元素,resultC(n,k)C(n, k)C(n,k) 个组合。
  • 空间复杂度为 O(k+C(n,k))O(k + C(n, k))O(k+C(n,k)),主要受结果存储影响。

通过回溯 + 剪枝高效解决组合枚举问题。回溯实现了所有可能的选择与撤销,剪枝优化减少了无效递归,确保算法在合理时间内运行。

http://www.xdnf.cn/news/18712.html

相关文章:

  • 人工智能时代下普遍基本收入(UBI)试验的实践与探索——以美国硅谷试点为例
  • LeetCode Hot 100 第二天
  • Java—— 配置文件Properties
  • 【Java SE】抽象类、接口与Object类
  • 秋招面试准备
  • 设计模式详解
  • TypeScript变量声明讲解
  • 个人思考与发展
  • 快速了解命令行界面(CLI)的行编辑模式
  • docker:compose
  • 【PSINS工具箱】MATLAB例程,平面上的组合导航,观测量为位置、速度、航向角,共5维。状态量为经典的15维
  • ModbusTCP与EtherNet/IP协议转换:工控机驱动步进电机完整教程
  • 智慧矿山误报率↓83%!陌讯多模态融合算法在矿用设备监控的落地优化
  • 安装即是已注册,永久可用!
  • Sql server的行转列
  • 数据结构:顺序表
  • C# 项目“交互式展厅管理客户端“针对的是“.NETFramework,Version=v4.8”,但此计算机上没有安装它。
  • 玳瑁的嵌入式日记D24-0823(数据结构)
  • 【基础-判断】使用http模块发起网络请求时,必须要使用on(‘headersReceive’)订阅请求头,请求才会成功。
  • 游戏广告投放数据分析项目:拆解投放的“流量密码”
  • 图像边缘检测
  • qwen2.5vl(2):lora 微调训练及代码讲解
  • Android Studio下载gradle文件很慢的捷径之路
  • 个人禁食伴侣FastTrack
  • 数据库类型与应用场景全解析:从传统关系型到新兴向量数据库
  • MySQL深分页的处理方案
  • React学习(十一)
  • 深入理解 React useEffect
  • 三、Bpmnjs 核心组件与架构介绍
  • 【c++进阶系列】:万字详解多态