解决LeetCode 47. 全排列 II 问题的正确姿势:深入分析剪枝与状态跟踪

news2025/5/18 11:30:45

文章目录

    • 问题描述
    • 常见错误代码与问题分析
      • 错误代码示例
      • 错误分析
    • 正确解决方案
      • 修正后的代码
      • 关键修正点
    • 核心逻辑详解
      • 1. 为何使用 `boolean[] used` 而非 `HashSet`?
      • 2. 剪枝条件 `!used[i - 1]` 的作用
    • 场景对比:何时用数组?何时用哈希表?
    • 实例分析
    • 总结

问题描述

给定一个可能包含重复元素的整数数组 nums,返回所有可能的唯一全排列。例如,输入 nums = [1,1,2],期望输出为:
[[1,1,2], [1,2,1], [2,1,1]]


常见错误代码与问题分析

错误代码示例

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        backTrack(result, new ArrayList<>(), nums);
        return result;
    }

    public void backTrack(List<List<Integer>> result, List<Integer> path, int[] nums) {
        if (path.size() == nums.length) {
            result.add(new ArrayList(path));
            return;
        }
        HashSet<Integer> used = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            if (i > 0 && nums[i] == nums[i - 1] || used.contains(nums[i])) {
                continue;
            }
            path.add(nums[i]);
            used.add(nums[i]);
            backTrack(result, path, nums);
            path.remove(path.size() - 1);
        }
    }
}

错误分析

  1. 错误使用 HashSet 跟踪元素
    HashSet 仅通过值去重,无法区分相同值的不同索引。例如,在 nums = [1,1,2] 中,两个 1 会被视为重复,导致合法排列 [1,1,2] 被错误跳过。

  2. 剪枝条件不完整
    原代码的剪枝条件 i > 0 && nums[i] == nums[i - 1] 未考虑元素的使用状态,无法正确避免同一层递归中的重复分支。


正确解决方案

修正后的代码

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if (nums == null || nums.length == 0) return result;
        Arrays.sort(nums);
        backtrack(result, new ArrayList<>(), nums, new boolean[nums.length]);
        return result;
    }

    private void backtrack(List<List<Integer>> result, List<Integer> path, int[] nums, boolean[] used) {
        if (path.size() == nums.length) {
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (used[i]) continue;
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;
            
            used[i] = true;
            path.add(nums[i]);
            backtrack(result, path, nums, used);
            path.remove(path.size() - 1);
            used[i] = false;
        }
    }
}

关键修正点

  1. 引入 boolean[] used 数组
    通过索引精确记录元素是否被使用,区分相同值的不同位置。

  2. 完善剪枝条件
    使用 i > 0 && nums[i] == nums[i - 1] && !used[i - 1] 确保仅在相同值的元素未被使用时剪枝,避免重复分支。


核心逻辑详解

1. 为何使用 boolean[] used 而非 HashSet

  • 区分相同值的不同索引
    例如 nums = [1,1,2],通过 used 数组可明确标记第一个 1(索引0)和第二个 1(索引1)的使用状态。
  • 时间复杂度与空间效率
    数组的索引访问为 O(1),且内存连续,无哈希表动态扩容的开销。

2. 剪枝条件 !used[i - 1] 的作用

  • 避免同一层递归的重复分支
    nums[i] == nums[i-1] 且前一个元素未被使用时(!used[i-1]),说明已存在以该值开头的分支,需跳过当前元素。
  • 允许深层递归使用相同值
    若前一个元素已被使用(used[i-1] = true),则当前处于深层递归,允许生成 [1,1,2] 等合法排列。

场景对比:何时用数组?何时用哈希表?

场景数组(boolean[])哈希表(HashSet)
是否需要区分相同值的不同位置✔️(如全排列问题)❌(仅判断值是否存在)
数据范围小且连续(如 n ≤ 1e6大或不连续
时间复杂度O(1)(直接索引访问)O(1)(平均情况)
空间复杂度固定空间动态扩展
适用问题类型排列、组合、子集(含重复元素)去重、存在性判断(如两数之和)

实例分析

nums = [1,1,2] 为例:

  1. 排序后数组[1,1,2]
  2. 第一层递归:选择第一个 1(索引0),标记 used[0] = true
  3. 第二层递归:允许选择第二个 1(索引1),标记 used[1] = true
  4. 第三层递归:选择 2,生成排列 [1,1,2]
  5. 回溯后剪枝:若尝试在第一层选择第二个 1,触发 !used[0] 剪枝条件,避免重复。

总结

  • 优先使用数组:当需要区分相同值的不同位置或数据范围较小时。
  • 灵活选择哈希表:当仅需判断值的存在性且数据稀疏时。
  • 剪枝条件是处理重复元素的关键,需结合元素值和索引状态综合判断。

通过合理选择数据结构和剪枝策略,可高效解决全排列 II 及其他回溯问题。

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

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

相关文章

【Docker】CentOS 8.2 安装Docker教程

目录 1.卸载 2.安装依赖 3.设置yum源 4.安装Docker 5.启动Docker 6.设置Docker开机自启 7.验证Docker是否安装成功 8.配置多个国内镜像地址 9.重启Docker 10.Docker指令大全 10.1.启动与关闭Docker 10.2.Docker镜像操作 10.3.Docker容器操作 10.4.Docker Compose操作…

K230 ISP:一种新的白平衡标定方法

第一次遇见需要利用光谱响应曲线进行白平衡标定的方法。很好奇是如何利用光谱响应曲线进行白平衡标定的。 参考资料参考&#xff1a;K230 ISP图像调优指南 K230 介绍 嘉楠科技 Kendryte 系列 AIoT 芯片中的最新一代 AIoT SoC K230 芯片采用全新的多核异构单元加速计算架构&a…

桃芯ingchips——windows HID键盘例程无法同时连接两个,但是安卓手机可以的问题

目录 环境 现象 原理及解决办法 环境 PC&#xff1a;windows11 安卓&#xff1a;Android14 例程使用的是HID Keyboard&#xff0c;板子使用的是91870CQ的开发板&#xff0c;DB870CC1A 现象 连接安卓手机时并不会出现该现象&#xff0c;两个开发板都可以当做键盘给手机发按…

[Linux] vim及gcc工具

目录 一、vim 1.vim的模式 2.vim的命令集 (1):命令模式 (2):底行模式 3.vim配置 二、gcc 1.gcc格式及选项 2.工作布置 三、自动化构建工具makefile 1.基本使用方法 2.配置文件解析 3.拓展 在linux操作系统的常用工具中&#xff0c;常用vim来进行程序的编写&#xff1b…

MySQL只操作同一条记录也会死锁吗?

大家好&#xff0c;我是锋哥。今天分享关于【MySQL只操作同一条记录也会死锁吗?】面试题。希望对大家有帮助&#xff1b; MySQL里where条件的顺序影响索引使用吗&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在MySQL中&#xff0c;死锁通常发生在多…

数据结构与算法——双向链表

双向链表 定义链表分类双向链表&#xff1a;带头双向循环链表 初始化打印尾插头插尾删头删查找在pos(指定位置)之后插入结点在pos(指定位置)之前插入结点删除pos(指定位置)的结点销毁顺序表与链表的分析 定义 链表分类 单向和双向 带头和不带头 带头是指存在一个头结点&…

MODBUS RTU调试助手使用方法详解

一、软件简介 485调试助手是一款常用的串口通信调试工具&#xff0c;专门用于RS-485总线设备的测试、调试和通信监控。它支持多种串口参数设置&#xff0c;提供数据收发功能&#xff0c;是工业现场调试的必备工具之一。 二、软件安装与启动 1. 系统要求 Windows 7/10/11操作…

自由学习记录(60)

Lecture 16 Ray Tracing 4_哔哩哔哩_bilibili 老师说的“高频采样”问题是什么&#xff1f; 现在考虑一个特殊情况&#xff1a; ❗ 一个像素内&#xff0c;图像信号变化很剧烈&#xff08;高频&#xff09;&#xff1a; 比如&#xff1a; 细网格纹理 马赛克背景 很高频的…

现代计算机图形学Games101入门笔记(三)

三维变换 具体形式缩放&#xff0c;平移 特殊点旋转。这里涉及到坐标系&#xff0c;先统一定义右手坐标系&#xff0c;根据叉乘和右手螺旋判定方向。这里还能法线Ry Sina 正负与其他两个旋转不一样。这里可以用右手螺旋&#xff0c;x叉乘z&#xff0c;发现大拇指朝下&#xff0…

WeakAuras Lua Script <BiaoGe>

WeakAuras Lua Script <BiaoGe> 表格拍卖插件WA字符串 表格字符串代码&#xff1a; !WA:2!S3xA3XXXrcoE2VH9l7ZFy)C969PvDpSrRgaeuhljFlUiiSWbxaqXDx(4RDd0vtulB0fMUQMhwMZJsAO5HenLnf1LPSUT4iBrjRzSepL(pS)e2bDdWp5)cBEvzLhrMvvnAkj7zWJeO7mJ8kYiJmYiImYF0b(XR)JR9JRD…

chrome 浏览器插件 myTools, 日常小工具。

1. 起因&#xff0c; 目的: 比如&#xff0c;chatgpt, google&#xff0c; 打开网页&#xff0c;就能直接输入文字&#xff0c;然后 grok 就不行&#xff0c;必须用鼠标点一下&#xff0c;才能输入文字。 对我而言&#xff0c;是个痛点&#xff01;写个插件&#xff0c;自动点…

智慧校园(含实验室)智能化专项汇报方案

该方案聚焦智慧校园(含实验室)智能化建设,针对传统实验室在运营监管、环境监测、安全管控、排课考勤等方面的问题,依据《智慧校园总体框架》等标准,设计数字孪生平台、实验室综合管理平台、消安电一体化平台三大核心平台,涵盖通信、安防、建筑设备管理等设施,涉及 395 个…

第三十四节:特征检测与描述-SIFT/SURF 特征 (专利算法)

一、特征检测:计算机视觉的基石 在计算机视觉领域中,特征检测与描述是实现图像理解的核心技术。就像人类通过识别物体边缘、角点等特征来认知世界,算法通过检测图像中的关键特征点来实现: 图像匹配与拼接 物体识别与跟踪 三维重建 运动分析 其中,SIFT(Scale-Invariant F…

【前端优化】vue2 webpack4项目升级webpack5,大大提升运行速度

记录一下过程 手里有个老项目&#xff0c;vue2webpack4 项目很大&#xff0c;每次运行、运行都要将近10分钟 现在又要往里面写很多东西&#xff0c;再不优化&#xff0c;开发着会更难受&#xff0c;所以决定先将它升级至webpack5 最初失败的尝试 直接在项目里安装了webpack5 但…

Nginx应用场景详解与配置指南

1. 什么是Nginx&#xff1f; Nginx&#xff08;发音为"engine-x"&#xff09;是一个高性能的HTTP和反向代理服务器&#xff0c;也是一个IMAP/POP3/SMTP代理服务器。它以高性能、稳定性、丰富的功能集、简单的配置和低资源消耗而闻名。 2. Nginx的主要应用场景 2.1 …

vue2 切换主题色以及单页面好使方法

今天要新增一个页面要根据不同公司切换不同页面主题色&#xff0c;一点一点来&#xff0c;怎么快速更改 el-pagination 分页组件主题色。 <el-pagination :page-size"pageSize" :pager-count"pageCount"layout"sizes, prev, pager, next, jumper,…

JavaScript【6】事件

1.概述&#xff1a; 在 JavaScript 中&#xff0c;事件&#xff08;Event&#xff09;是浏览器或 DOM&#xff08;文档对象模型&#xff09;与 JavaScript 代码之间交互的一种机制。它代表了在浏览器环境中发生的特定行为或者动作&#xff0c;比如用户点击鼠标、敲击键盘、页面…

STM32F10xx 参考手册

6. 什么是寄存器 本章参考资料&#xff1a;《STM32F10xx 参考手册》、《STM32F10xx数据手册》、 学习本章时&#xff0c;配合《STM32F10xx 参考手册》“存储器和总线架构”及“通用I/O(GPIO)”章节一起阅读&#xff0c;效果会更佳&#xff0c;特别是涉及到寄存器说明的部分。…

TIFS2024 | CRFA | 基于关键区域特征攻击提升对抗样本迁移性

Improving Transferability of Adversarial Samples via Critical Region-Oriented Feature-Level Attack 摘要-Abstract引言-Introduction相关工作-Related Work提出的方法-Proposed Method问题分析-Problem Analysis扰动注意力感知加权-Perturbation Attention-Aware Weighti…

Redis 发布订阅模式深度解析:原理、应用与实践

在现代分布式系统架构中&#xff0c;实时消息传递机制扮演着至关重要的角色。Redis 作为一款高性能的内存数据库&#xff0c;其内置的发布订阅(Pub/Sub)功能提供了一种轻量级、高效的消息通信方案。本文将全面剖析 Redis 发布订阅模式&#xff0c;从其基本概念、工作原理到实际…