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

学习记录:js算法(八十四):子集 II

文章目录

    • 子集 II
      • 思路一
      • 思路二

子集 II

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的
子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。


思路一

function subsetsWithDup(nums) {
    nums.sort((a, b) => a - b); // 先排序
    const result = [];
    const backtrack = (start, path, lastSelected = -1) => {
        // 将当前子集添加到结果集中
        result.push([...path]);
        
        for (let i = start; i < nums.length; i++) {
            // 跳过重复元素
            if (i > start && nums[i] === nums[i - 1] && lastSelected !== i - 1) continue;
            // 选择当前元素
            path.push(nums[i]);
            backtrack(i + 1, path, i); // 注意传递当前元素的下标
            // 回溯,撤销选择
            path.pop();
        }
    };
    
    backtrack(0, []);
    return result;
}

讲解
要解决这个问题,我们可以基于之前提到的回溯算法思路进行扩展,以处理包含重复元素的情况。关键在于,在递归过程中增加一个判断条件来跳过已经处理过的重复元素,以避免生成重复的子集。

  1. 排序数组:首先对原数组进行排序,这样相同的元素会被排列在一起,便于我们在递归过程中跳过重复的元素。
  2. 定义递归函数:递归函数需要接收当前子集、当前遍历到的数组下标以及上一个被选中元素的下标作为参数。
  3. 递归终止条件:当遍历到数组末尾时,将当前子集添加到结果集中,然后返回。
  4. 单层递归逻辑:
    ○ 如果当前元素与前一个被选中元素相同,并且前一个元素没有被选中,则跳过当前元素(避免生成重复子集)。
    ○ 将当前元素加入子集,然后递归调用下一个元素。
    ○ 回溯:从子集中移除当前元素(即不选择当前元素),然后递归调用下一个元素。

思路二

var subsetsWithDup = function (nums) {
    const result = [];
    const n = nums.length;
    nums.sort((a, b) => a - b); // 先对数组进行排序

    const totalSubsets = 1 << n; // 2^n
    const seen = new Set(); // 用于去重

    for (let i = 0; i < totalSubsets; i++) {
        const subset = [];
        for (let j = 0; j < n; j++) {
            if (i & (1 << j)) { // 检查第 j 位是否为 1
                subset.push(nums[j]);
            }
        }
        // 将子集转换为字符串形式以便于去重
        const subsetStr = subset.join(',');
        if (!seen.has(subsetStr)) {
            seen.add(subsetStr);
            result.push(subset);
        }
    }

    return result;
};

讲解

  1. 函数定义
    subsetsWithDup 是一个用于生成包含重复元素的数组的所有子集的函数。它接受一个整数数组 nums 作为参数。
  2. 初始化
    函数开始时,定义一个空数组 result 用于存储所有生成的子集,并获取数组 nums 的长度 n
  3. 排序
    在生成子集之前,首先对数组 nums 进行排序。排序的目的是为了确保相同的元素是相邻的,这样在后续处理时能够方便地去重。
  4. 计算总子集数
    接下来,计算总的子集数量,这个数量为 2^n。使用左移运算符 1 << n 来实现这一点。这个计算表明,对于 n 个元素,我们有 2^n 种可能的子集组合。
  5. 去重集合
    定义一个集合 seen,用于存储已经生成的子集,以避免重复的子集被添加到结果中。
  6. 遍历所有可能的子集
    使用一个循环,从 0totalSubsets - 1 遍历所有可能的子集。每次循环开始时,初始化一个空数组 subset 用于存储当前生成的子集。
  7. 生成子集
    在内部的循环中,遍历从 0n - 1 的每个元素。通过位运算检查当前循环变量 i 的每一位是否为 1。如果某一位为 1,则将对应的元素 nums[j] 添加到当前子集 subset 中。
  8. 转换和去重
    生成当前子集后,将其转换为字符串形式,以便于在集合中进行去重。通过 join(',') 方法将 subset 数组的元素连接成一个字符串。接下来,检查该字符串是否已经存在于 seen 集合中。如果不存在,则将其添加到 seen 集合中,并将当前子集 subset 添加到结果数组 result 中。
  9. 返回结果
    最后,函数返回结果数组 result,其中包含所有唯一的子集。

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

相关文章:

  • PostgreSQL技术内幕22:vacuum full 和 vacuum
  • MySQL 中删除重复数据 SQL 写法
  • 优先级队列(算法十四)
  • Vue Diff 算法完全解析
  • 【深度学习】Pytorch:调度器与学习率衰减
  • 论文笔记(四十七)Diffusion policy: Visuomotor policy learning via action diffusion(下)
  • vue系列==vue组件
  • sparkSQL面试题
  • Go语言sync.WaitGroup与errgroup.Group用法详解
  • 迅为itop-3568开发板AMP双系统使用手册之烧写AMP镜像
  • 力扣第33题:搜索旋转排序数组
  • 聚水潭数据集成到MySQL的技术实操与解决方案
  • Vue前端开发:事件对象参数
  • Docker-安装
  • Flutter UI架构(3)
  • gulp入门教程18:gulp插件gulp-clean
  • RLHF中,人类反馈数据格式是什么样的?
  • PostgreSQL 取前一列不为 NULL
  • 程序《工资分类收税》
  • 2024/11/3 随笔笔记
  • 深度学习笔记之BERT(一)BERT的基本认识
  • 利用Spring Boot框架打造信息学科平台
  • Golang | Leetcode Golang题解之第520题检测大写字母
  • GitHub、Gitee、GitLab介绍
  • [spring源码]spring推断构造方法
  • 【深入浅出】深入浅出Bert(附面试题)