网站推荐、资源下载等 | 个人网站

DFS算法-求集合的所有子集


1. 题目来源

牛客网,集合的所有子集(一)
https://www.nowcoder.com/practice/c333d551eb6243e0b4d92e37a06fbfc9
image

2. 普通方法

1. 思路

数学上排列组合中的组合,从N个元素的集合中拿出M(0≤ M ≤ N)个元素的可能数,标记为image
。M从0开始遍历到N,就是所求的所有子集合。这里要结果不是数,而且取的元素集合。

这里具体在做的就是用遍历索引的方式取出想要的集合

  • 比如数组为{1, 2, 3, 4, 5},要取0个数,那么所有可能就是{}。1个集合
  • 比如数组为{1, 2, 3, 4, 5},要取1个数,那么所有可能就是{{1}, {2}, {3}, {4}, {5}}。5个集合
  • 比如数组为{1, 2, 3, 4, 5},要取2个数,那么所有可能就是{{1, 2}, {1, 3}, {1, 4}, {1, 5}, {2, 3}, {2, 4}, {2, 5}, {3, 4}, {3, 5}, {4, 5}}。10个集合
  • 比如数组为{1, 2, 3, 4, 5},要取3个数,那么所有可能就是{{1, 2, 3}, {1, 2, 4}, {1, 2, 5}, {1, 3, 4}, {1, 3, 5}, {1, 4, 5}, {2, 3, 4}, {2, 3, 5}, {2, 4, 5}, {3, 4, 5}}。10个集合
  • 比如数组为{1, 2, 3, 4, 5},要取4个数,那么所有可能就是{{1, 2, 3, 4}, {1, 2, 3, 5}, {1, 2, 4, 5}, {1, 3, 4, 5}, {2, 3, 4, 5}}。5个集合
  • 比如数组为{1, 2, 3, 4, 5},要取5个数,那么所有可能就是{{1, 2, 3, 4, 5}}。1个集合

2. 代码

import java.util.ArrayList;

/**
 * @className SolutionTest
 * @description
 * @author liwei
 * @date 2022/9/8 15:29
 * @version V1.0
 **/
public class SolutionTest {

    public static void main(String[] args) {
        int[] ints = {5, 4, 3, 2, 1};
        System.out.println(subsets(ints));
    }

    public static ArrayList<ArrayList<Integer>> subsets(int[] ints) {
        // 插入排序,升序排列
        for (int i = 0, length = ints.length; i < length - 1; i++) {
            int tmpValue = ints[i];
            int tmpIndex = i;
            for (int j = i + 1; j < ints.length; j++) {
                if (tmpValue > ints[j]) {
                    tmpValue = ints[j];
                    tmpIndex = j;
                }
            }
            if (i != tmpIndex) {
                ints[tmpIndex] = ints[i];
                ints[i] = tmpValue;
            }
        }

        ArrayList<ArrayList<Integer>> lists = new ArrayList<>();

        // 循环,需要从数组中取i个数,然后取出i个数的所有可能。
        // 比如数组为{1, 2, 3, 4, 5},要取1个数,那么所有可能就是{{1}, {2}, {3}, {4}, {5}}
        // 比如数组为{1, 2, 3, 4, 5},要取2个数,那么所有可能就是{{1, 2}, {1, 3}, {1, 4}, {1, 5}, {2, 3}, {2, 4}, {2, 5}, {3, 4}, {3, 5}, {4, 5}}
        for (int number = 0; number <= ints.length; number++) {
            lists.addAll(subsets(ints, number));
        }

        return lists;
    }

    /**
     * 取多少个元素的子集集合
     *
     * @param ints
     *        数组
     * @param number
     *        要取的元素个数
     * @return java.util.ArrayList<java.util.ArrayList < java.lang.Integer>>
     * @author liwei
     * @date 2022/8/10 20:22
     */
    public static ArrayList<ArrayList<Integer>> subsets(int[] ints, int number) {
        ArrayList<ArrayList<Integer>> lists = new ArrayList<>();
        // 特殊处理,如果需要的个数为0,那么直接添加一个空集合,然后返回
        if (number <= 0) {
            lists.add(new ArrayList<>());
            return lists;
        }
        int length = ints.length;

        // 特殊处理,如果需要的个数大于等于数组元素个数,那么直接添加一个所有元素的集合,然后返回
        if (length <= number) {
            ArrayList<Integer> arrayList = new ArrayList<>();
            for (int i1 : ints) {
                arrayList.add(i1);
            }
            lists.add(arrayList);
            return lists;
        }

        // 用于存储取数组元素的下标。例如数组为[6,7,8,9],要取2个数,那么这里的indexs数组长度就为2,下面初始化索引为最开始索引,indexs=[0,1]
        int[] indexs = new int[number];
        // 初始化索引
        for (int j = 0; j < number; j++) {
            indexs[j] = j;
        }

        // 循环,索引数组的第一个值索引+个数>数组个数,就跳出循环
        while (indexs[0] + number <= length) {
            // 最后一个索引循环,比如最初indexs=[0,1],数组元素个数为4,那么这里循环,indexs=[0,1]、indexs=[0,2]、indexs=[0,3],跳出循环indexs=[0,4]
            for (int k = indexs[number - 1]; k < length; k++) {
                ArrayList<Integer> arrayList = new ArrayList<>();
                // 通过下标取数组中元素
                for (int index : indexs) {
                    arrayList.add(ints[index]);
                }
                lists.add(arrayList);
                // 最后一个索引往后移动
                indexs[number - 1]++;
            }

            // 上面索引数组中最后一个值已经到了最后,这里是把倒数第二个值+1,如果倒数第二也到了最后,那么再往前找,直到第一个。
            // 1、例如数组为[5,6,7,8,9],要取得元素个数为3,索引数组初始化为indexs=[0,1,2],经过上面最后一个索引循环变成indexs=[0,1,5],这里循环得作用就是把索引数组变成indexs=[0,2,3]
            // 2、如果经过上面最后一个索引循环变成indexs=[0,4,5],这里循环得作用就是把索引数组变成indexs=[1,2,3]
            for (int l = number - 2; l >= 0; l--) {
                // 索引数组只修改一次就跳出循环
                if (indexs[l] < indexs[l + 1] - 1) {
                    indexs[l]++;
                    for (int m = l + 1; m < number; m++) {
                        indexs[m] = indexs[m - 1] + 1;
                    }
                    break;
                }
            }
        }
        return lists;
    }
}

3. 运行结果

[[], [1], [2], [3], [4], [5], [1, 2], [1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5], [3, 4], [3, 5], [4, 5], [1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 5], [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5], [1, 2, 3, 4, 5]]

image

3. DFS算法

Depth-First Search深度优先搜索算法

1. 概念

在深度优先搜索中,对于最新发现的顶点,如果它还有以此为顶点而未探测到的边,就沿此边继续探测下去,当顶点v的所有边都已被探寻过后,搜索将回溯到发现顶点v有起始点的那些边。
这一过程一直进行到已发现从源顶点可达的所有顶点为止。如果还存在未被发现的顶点,则选择其中一个作为源顶点,并重复上述过程。
整个过程反复进行,直到所有的顶点都被发现时为止。

比如数组{1,2,3,4,5}所有子集合,这里不包括空集合
image

2. 解题思路

如下图,探索和回溯,探索是实线箭头表示,回溯是虚线箭头表示。其中虚线箭头指向中间表示这个节点还有其他子节点没有探索完,虚线箭头指向右边便是这个节点已经探索完成。
image

在这里我们从最右开始,就会发现里面的递归。数组{1,2,3,4,5}的所有子集合划分为几部分:

  1. 含有元素1的所有集合。【发现在下面第2、3、4、5基础上加上空集合,然后每个集合都加上元素1,就是此时要求的所有集合】

【{1}, {1, 2}, {1, 2, 3}, {1, 2, 3, 4}, {1, 2, 3, 4, 5}, {1, 2, 3, 5}, {1, 2, 4}, {1, 2, 4, 5}, {1, 2, 5}, {1, 3}, {1, 3, 4}, {1, 3, 4, 5}, {1, 3, 5}, {1, 4}, {1, 4, 5}, {1, 5}】,16个集合

  1. 含有元素2且不包含元素1的所有集合。【发现在下面第3、4、5基础上加上空集合,然后每个集合都加上元素2,就是此时要求的所有集合】

【{2}, {2, 3}, {2, 3, 4}, {2, 3, 4, 5}, {2, 3, 5}, {2, 4}, {2, 4, 5}, {2, 5}】,8个集合

  1. 含有元素3且不包含元素1和元素2的所有集合。【发现在下面第4、5基础上加上空集合,然后每个集合都加上元素3,就是此时要求的所有集合】

【{3}, {3, 4}, {3, 4, 5}, {3, 5}】,4个集合

  1. 含有元素4且不包含元素1、元素2和元素3的所有集合。【发现在下面第5基础上加上空集合,然后每个集合都加上元素4,就是此时要求的所有集合】

【{4}, {4, 5}】,2个集合

  1. 含有元素5且不包含元素1、元素2、元素3和元素4的所有集合。【此时只有一个元素5,那么就只有一个集合,集合里面就一个元素5】

【{5}】,1个集合

image

3. 代码

import java.util.ArrayList;

/**
 * @className FDSTest
 * @description FDS算法在求集合的所有子集合,包括空集合时的应用
 * @author liwei
 * @date 2022/9/8 15:29
 * @version V1.0
 **/
public class FDSTest {

    public static void main(String[] args) {
        int[] ints = {5, 4, 3, 2, 1};
        System.out.println(subsets(ints));
    }

    /**
     * @description 求数组的所有子集合,包括空数组。按要求升序排序数组
     * @author liwei
     * @date 2022/9/9 11:45
     * @param ints
     *        数组
     * @return java.util.ArrayList<java.util.ArrayList<java.lang.Integer>>
     **/
    public static ArrayList<ArrayList<Integer>> subsets(int[] ints) {
        // 插入排序,升序排列
        for (int i = 0, length = ints.length; i < length - 1; i++) {
            int tmpValue = ints[i];
            int tmpIndex = i;
            for (int j = i + 1; j < ints.length; j++) {
                if (tmpValue > ints[j]) {
                    tmpValue = ints[j];
                    tmpIndex = j;
                }
            }
            if (i != tmpIndex) {
                ints[tmpIndex] = ints[i];
                ints[i] = tmpValue;
            }
        }

        // 调用FDS
        return subsetsFDS(ints);
    }

    /**
     * @description FDS算法求解数组的所有子集合,包括空数组,内含递归
     * @author liwei
     * @date 2022/9/9 11:47
     * @param ints
     *        数组
     * @return java.util.ArrayList<java.util.ArrayList<java.lang.Integer>>
     **/
    public static ArrayList<ArrayList<Integer>> subsetsFDS(int[] ints) {
        ArrayList<ArrayList<Integer>> arrayList = new ArrayList<>();
        if (null == ints || ints.length <= 0) {
            // 递归到最后没有元素的话返回空集合
            arrayList.add(new ArrayList<>());
            return arrayList;
        }

        // 取第一个元素
        int one = ints[0];
        // 定义取完第一个元素后剩余数组长度
        int[] twoArray = new int[ints.length - 1];

        for (int i = 1; i < ints.length; i++) {
            twoArray[i - 1] = ints[i];
        }

        // 递归,取出第一个元素后,递归得到不包含第一个元素的所有子集合
        ArrayList<ArrayList<Integer>> listArrayList = subsetsFDS(twoArray);
        for (ArrayList<Integer> integers : listArrayList) {
            // 把集合复制一份保存到返回集合中
            arrayList.add(new ArrayList<>(integers));

            // 在集合第一位置添加第一个元素,然后保存到返回集合中
            integers.add(0, one);
            arrayList.add(integers);
        }

        return arrayList;
    }
}

4. 运行结果

[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3], [4], [1, 4], [2, 4], [1, 2, 4], [3, 4], [1, 3, 4], [2, 3, 4], [1, 2, 3, 4], [5], [1, 5], [2, 5], [1, 2, 5], [3, 5], [1, 3, 5], [2, 3, 5], [1, 2, 3, 5], [4, 5], [1, 4, 5], [2, 4, 5], [1, 2, 4, 5], [3, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5], [1, 2, 3, 4, 5]]

image

4. 对比

  1. 普通方法的优势:如果是指定取M个数,如上面例子我们只取出只有2个元素的所有集合;
  2. DFS算法的优势:这里只是这个算法的一种体现,最重要的是算法思想。例如求2点间最短距离;
posted @ 2022-09-09 15:31  xiaostudy  阅读(147)  评论(0编辑  收藏  举报
网站推荐
[理工最爱]小时百科 |  GitHub |  Gitee |  开源中国社区 |  牛客网 |  不学网论坛 |  r2coding |  冷熊简历 |  爱盘 |  零散坑 |  bootstrap中文网 |  vue.js官网教程 |  源码分享站 |  maven仓库 |  楼教主网站 |  廖雪峰网站 |  w3cschool |  在线API |  代码在线运行 |  [不学网]代码在线运行 |  JS在线运行 |  PHP中文网 |  深度开源eclipse插件 |  文字在线加密解密 |  菜鸟教程 |  慕课网 |  千图网 |  手册网 |  素材兔 |  盘多多 |  悦书PDF |  sumatra PDF |  calibre PDF |  Snipaste截图 |  shareX截图 |  vlc-media-player播放器 |  MCMusic player |  IDM下载器 |  格式工厂 |  插件网 |  谷歌浏览器插件 |  Crx搜搜 |  懒人在线计算器 |  leetcode算法题库 |  layer官网 |  layui官网 |  formSelects官网 |  Fly社区 |  程序员客栈 |  融云 |  华为云 |  阿里云 |  ztree官网API |  teamviewer官网 |  sonarlint官网 |  editormd |  pcmark10官网 |  crx4chrome官网 |  apipost官网 |  花生壳官网 |  serv-u官网 |  杀毒eset官网 |  分流抢票bypass官网 |  懒猴子CG代码生成器官网 |  IT猿网 |  natapp[内网穿透] |  ngrok[内网穿透] |  深蓝穿透[内网穿透] |  WakeMeOnLan[查看ip] |  iis7 |  [漏洞扫描]Dependency_Check官网 |  [图标UI]fontawesome官网 |  idea插件官网 |  路过图床官网 |  sha256在线解密 |  在线正则表达式测试 |  在线文件扫毒 |  KuangStudy | 
资源下载
电脑相关: Windows原装下载msdn我告诉你 |  U盘制作微PE工具官网下载 |  Linux_CentOS官网下载 |  Linux_Ubuntu官网下载 |  Linux_OpenSUSE官网下载 |  IE浏览器官网下载 |  firefox浏览器官网下载 |  百分浏览器官网下载 |  谷歌google浏览器历史版本下载 |  深度deepin系统官网下载 |  中兴新支点操作系统官网下载 |  文件对比工具Beyond Compare官网下载 |  开机启动程序startup-delayer官网下载 |  openoffice官网下载 |  utorrent官网下载 |  qbittorrent官网下载 |  cpu-z官网下载 |  蜘蛛校色仪displaycal官网下载 |  单文件制作greenone下载 |  win清理工具Advanced SystemCare官网下载 |  解压bandizip官网下载 |  内存检测工具memtest官网下载 |  磁盘坏道检测与修复DiskGenius官网下载 |  磁盘占用可视化SpaceSniffer官网下载 |  [磁盘可视化]WizTree官网下载 |  win快速定位文件Everything官网下载 |  文件定位listary官网下载 |  动图gifcam官网下载 |  7-Zip官网下载 |  磁盘分区工具diskgenius官网下载 |  CEB文件查看工具Apabi Reader官网下载 |  罗技鼠标options官网下载 |  [去除重复文件]doublekiller官网下载 | 
编程相关: ApacheServer官网下载 |  Apache官网下载 |  Git官网下载 |  Git高速下载 |  Jboss官网下载 |  Mysql官网下载 |  Mysql官网历史版本下载 |  NetBeans IDE官网下载 |  Spring官网下载 |  Nginx官网下载 |  Resin官网下载 |  Tomcat官网下载 |  jQuery历史版本下载 |  nosql官网下载 |  mongodb官网下载 |  mongodb_linux历史版本下载 |  mongodb客户端下载 |  VScode官网下载 |  cxf官网下载 |  maven官网下载 |  QT官网下载 |  SVN官网下载 |  SVN历史版本下载 |  nodeJS官网下载 |  oracle官网下载 |  jdk官网下载 |  STS官网下载 |  STS历史版本官网下载 |  vue官网下载 |  virtualbox官网下载 |  docker desktop官网下载 |  github desktop官网下载 |  EditPlus官网下载 |  zTree下载 |  layui官网下载 |  jqgrid官网下载 |  jqueryui官网下载 |  solr历史版本下载 |  solr分词器ik-analyzer-solr历史版本下载 |  zookeeper历史版本官网下载 |  nssm官网下载 |  elasticsearch官网下载 |  elasticsearch历史版本官网下载 |  redis官网下载 |  redis历史版本官网下载 |  redis的win版本下载 |  putty官网下载 |  查看svn密码TSvnPD官网下载 |  MongoDB连接工具Robo官网下载 |  dll查看exescope官网下载 |  dll2c官网下载 |  接口测试apipost官网下载 |  接口测试postman官网下载 |  原型设计工具AxureRP官网下载 |  canal官网下载 |  idea主题样式下载 |  vue的GitHub下载 |  finalShell官网下载 |  ETL工具kafka官网下载 |  cavaj[java反编译]官网下载 |  jd-gui[java反编译]官网下载 |  radmin[远程连接]官网下载 |  tcping[win ping端口]下载 |  jQueryUploadFile官网下载 |  RedisPlus下载 |  aiXcoder智能编程助手官网下载 |  [表单效验]validform官网下载 |  idea官网下载 |  RedisStudio下载 |  MD转word含公式pandoc官网下载 |  logviewer官网下载 |  Kafka官网下载 |  hbase高速下载 |  hadoop官网下载 |  hadooponwindows的GitHub下载 |  hive官网下载 |  soapui官网下载 |  flink官网下载 |  kafkatool官网下载 |  MinIO官网下载 |  MinIO中国镜像下载 | 
办公相关工具
免费在线拆分PDF【不超过30M】 |  免费在线PDF转Word【不超过10M】 |  在线文字识别转换【不超过1M】 |  PDF转换成Word【不超过50M】 |  在线OCR识别 |  Smallpdf |  文件转换器Convertio |  迅捷PDF转换器 |  字母大小写转换工具 |  档铺 |  快传airportal[可文字] |  快传-文叔叔 |  P2P-小鹿快传 |  [图床]ImgURL | 
网站入口
腾讯文档 |  有道云笔记网页版 |  为知笔记网页版 |  印象笔记网页版 |  蓝奏云 |  QQ邮箱 |  MindMaster在线思维导图 |  bilibili |  PDM文件在线打开 |  MPP文件在线打开 |  在线PS软件 |  在线WPS |  阿里云企业邮箱登陆入口 | 
其他
PDF转换 |  悦书PDF转换 |  手机号注册查询 |  Reg007 |  akmsg |  ip8_ip查询 |  ipip_ip查询 |  天体运行testtubegames |  测试帧率 |  在线网速测试 |