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

去重判断 是!vis[i - 1] 而非 vis[i - 1] 详解

为什么去重判断是 !vis[i - 1] 而非 vis[i - 1]

在处理全排列时,尤其是含有重复字符的场景(如 AAB),为了避免重复的排列结果,需要在递归过程中跳过某些重复的选择。判断条件 chars[i] == chars[i - 1] && !vis[i - 1] 的设计是为了确保当前重复字符只有在前一个相同字符已经被选择的情况下才可以继续选择。具体原因详解如下:


去重的核心逻辑

1. 排序是前提

将输入字符串或数组排序(例如,AAB 排序为 ['A', 'A', 'B']),确保相同字符相邻。这样可以方便地判断当前字符是否与前一个字符重复。

2. chars[i] == chars[i - 1]
  • 这一部分检查当前字符是否与前一个字符相同。
  • 如果相同,说明是重复字符,需要进一步判断是否跳过。
3. !vis[i - 1] 的意义
  • 如果 !vis[i - 1]true(即前一个字符尚未被访问),说明当前递归分支还未处理过重复字符的排列,此时跳过当前字符以避免重复。
  • 如果 vis[i - 1]true,说明前一个字符已经被访问并处理过,当前字符可以作为后续递归的一部分。
核心思想

!vis[i - 1] 的目的是确保:

  • 每一层递归中,同一层的重复字符只选择一次。
  • 后续递归中,重复字符仍可以被合法选择。

为什么不是 vis[i - 1]

如果去重条件写为 chars[i] == chars[i - 1] && vis[i - 1],问题在于:

  • 逻辑错误:当 vis[i - 1] == true 时,当前字符实际上已经被递归分支合法处理过,但条件却要求跳过,这会导致遗漏某些合法的排列。
  • 例如,对于输入字符串 AAB,某些递归分支可能会被错误跳过,结果不完整。

以具体递归为例

输入字符串:"AAB"

排序后:['A', 'A', 'B']

递归流程对比


正确逻辑:chars[i] == chars[i - 1] && !vis[i - 1]
层级当前选择条件检查 (!vis[i - 1])状态 (cur)结果
第1层A (0)-A
第2层A (1)前一个 A 已访问,合法AA
第3层B (2)-AAB生成排列 AAB
回溯AA
第2层B (2)-AB
第3层A (1)前一个 A 已访问,合法ABA生成排列 ABA
回溯A
第1层A (1)前一个 A 未访问,跳过-避免重复
第1层B (2)-B
第2层A (0)-BA
第3层A (1)前一个 A 已访问,合法BAA生成排列 BAA

最终结果: [AAB, ABA, BAA]


错误逻辑:chars[i] == chars[i - 1] && vis[i - 1]
层级当前选择条件检查 (vis[i - 1])状态 (cur)结果
第1层A (0)-A
第2层A (1)跳过-错误跳过合法分支
第2层B (2)-AB
第3层A (1)跳过-错误跳过合法分支

最终结果: [AB]
由于错误的条件跳过了合法分支,排列结果不完整。


!vis[i - 1] 的关键意义

  1. 递归层次控制

    • 在同一层递归中,重复字符只能由第一个未被访问的字符负责分支生成,避免重复排列。
    • 通过 !vis[i - 1] 跳过其他重复字符,减少冗余计算。
  2. 递归分支合法性

    • 在递归的下一层(或更深层次),前一个相同字符已经参与排列,当前字符可以作为下一层递归的一部分,无需跳过。
  3. 去重的前提是排序

    • 排序确保重复字符相邻,使得 chars[i] == chars[i - 1] 成立时,能够明确其去重逻辑是否需要跳过。

总结:为什么是 !vis[i - 1]

  1. 避免同层重复选择

    • 如果前一个相同字符未被访问(!vis[i - 1]),说明当前字符在同一层中属于重复选择,跳过当前字符。
  2. 允许后续分支合法访问

    • 如果前一个相同字符已经被访问(vis[i - 1]),说明当前字符是合法选择的一部分,可以继续递归。
  3. 排序+标记数组确保唯一性

    • 排序使相同字符相邻,标记数组 vis[] 确保字符是否已访问,从而有效去重。

所以,!vis[i - 1] 的判断方式是正确的去重逻辑核心,它保证了排列结果的唯一性和完整性。


http://www.kler.cn/a/409657.html

相关文章:

  • mysql数据库双机互为主从设置与数据库断电无法启动处理
  • qt+opengl 三维物体加入摄像机
  • 开发者视角下的鸿蒙
  • 项目进度计划表:详细的甘特图的制作步骤
  • MariaDB面试题及参考答案
  • ETAS工具导入DBC生成Com协议栈
  • 在 ARM 平台上如何实现Linux系统的1秒启动
  • 菊风视频能力平台开发服务正式入驻华为云云商店,成为华为云联营联运合作伙伴
  • GPT-4 Technical Report——GPT-4技术报告
  • Spring Boot Starter依赖
  • 【创建型设计模式】工厂模式
  • 心情追忆-首页“毒“鸡汤AI自动化
  • 使用go语言进行端口扫描
  • YOLOv8-ultralytics-8.2.103部分代码阅读笔记-tasks.py
  • 【JavaEE】Maven的介绍及配置
  • Flutter:启动屏逻辑处理02:启动页
  • 【从0学英语】字母发音指南:一套掌握所有字母的发音组合
  • NFS文件服务器
  • 基于开源 AI 智能名片 2+1 链动模式 S2B2C 商城小程序源码的社交新零售利益共同体构建与发展研究
  • Altium Designer学习笔记 21.PCB板框的评估及叠层设置
  • 视频监控实现画面缩放功能
  • 【数据结构-队列】力扣622. 设计循环队列
  • java-加密算法
  • 掌握 Vue key:剖析其原理及与无 key 的区别
  • 【Hive是什么?】Hadoop和Hive是什么关系?Hive在Hadoop上是怎么运行的?用大白话理解Hive和Hadoop的关系。
  • 亚马逊IP关联是什么?我们该怎么解决呢?