1. 题目来源

牛客网,集合的所有子集(一)

https://www.nowcoder.com/practice/c333d551eb6243e0b4d92e37a06fbfc9

2. 普通方法

1. 思路

数学上排列组合中的组合,从N个元素的集合中拿出M(0≤ M ≤ N)个元素的可能数,标记为

。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]]

3. DFS算法

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

1. 概念

在深度优先搜索中,对于最新发现的顶点,如果它还有以此为顶点而未探测到的边,就沿此边继续探测下去,当顶点v的所有边都已被探寻过后,搜索将回溯到发现顶点v有起始点的那些边。

这一过程一直进行到已发现从源顶点可达的所有顶点为止。如果还存在未被发现的顶点,则选择其中一个作为源顶点,并重复上述过程。

整个过程反复进行,直到所有的顶点都被发现时为止。

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

2. 解题思路

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

在这里我们从最右开始,就会发现里面的递归。数组{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个集合

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]]

4. 对比

  1. 普通方法的优势:如果是指定取M个数,如上面例子我们只取出只有2个元素的所有集合;
  2. DFS算法的优势:这里只是这个算法的一种体现,最重要的是算法思想。例如求2点间最短距离;

DFS算法-求集合的所有子集的更多相关文章

  1. 傻瓜方法求集合的全部子集问题(java版)

    给定随意长度的一个集合.用一个数组表示,如{"a", "b","c"},求它的全部子集.结果是{ {a}, {b}, {c}, {a,b}, ...

  2. Java 求集合的所有子集

    递归方法调用,求解集合的所有子集. package ch01; import java.util.HashSet; import java.util.Iterator; import java.uti ...

  3. 【SICP读书笔记(五)】练习2.32 --- 递归求集合子集

    题目内容: 我们可以将一个集合表示为一个元素互不相同的表,因此就可以将一个集合的所有子集表示为表的表.例如,假定集合为(1,2,3),它的所有子集的集合就是( () (3) (2) (2 3) (1) ...

  4. DFS 算法总结

    DFS 算法总结 这篇文章会对DFS进行一个总结,列举的题目则是从LeetCode上面选的: 适用场景: 有三个方面,分别是输入数据.状态转换图.求解目标: 输入数据:如果是递归数据结构,如单链表,二 ...

  5. [Leetcode] subsets 求数组所有的子集

    Given a set of distinct integers, S, return all possible subsets. Note: Elements in a subset must be ...

  6. KM(Kuhn-Munkres)算法求带权二分图的最佳匹配

    KM(Kuhn-Munkres)算法求带权二分图的最佳匹配 相关概念 这个算法个人觉得一开始时有点难以理解它的一些概念,特别是新定义出来的,因为不知道是干嘛用的.但是,在了解了算法的执行过程和原理后, ...

  7. Tarjan算法求割点

    (声明:以下图片来源于网络) Tarjan算法求出割点个数 首先来了解什么是连通图 在图论中,连通图基于连通的概念.在一个无向图 G 中,若从顶点i到顶点j有路径相连(当然从j到i也一定有路径),则称 ...

  8. BFS/DFS算法介绍与实现(转)

    广度优先搜索(Breadth-First-Search)和深度优先搜索(Deep-First-Search)是搜索策略中最经常用到的两种方法,特别常用于图的搜索.其中有很多的算法都用到了这两种思想,比 ...

  9. C++迪杰斯特拉算法求最短路径

    一:算法历史 迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法.是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题.迪杰斯特拉算法主要特点是以 ...

随机推荐

  1. jenkins部署docker

    1. 先在jenkins上配置拉取代码部分,需要在git上找到项目位置,直接复制url即可 http://192.168.0.161:3000/IT-Insurance/Back.Test-Walle ...

  2. NC16597 [NOIP2011]聪明的质监员

    NC16597 [NOIP2011]聪明的质监员 题目 题目描述 小T 是一名质量监督员,最近负责检验一批矿产的质量.这批矿产共有 \(n\) 个矿石,从 \(1\) 到 \(n\) 逐一编号,每个矿 ...

  3. 利用噪声构建美妙的 CSS 图形

    在平时,我非常喜欢利用 CSS 去构建一些有意思的图形. 我们首先来看一个简单的例子.首先,假设我们实现一个 10x10 的格子: 此时,我们可以利用一些随机效果,优化这个图案.譬如,我们给它随机添加 ...

  4. 解决报错ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/run/mysqld/mysqld.pid (code

    问题的由来 MySQL服务没有正常关机,是电脑没电后自动关机产生,记录一下排查过程 1.本以为是pid的问题,上网找了教程,解决不了,然后看日志看了网上各种说是数据库内存溢出 2021-03-12T1 ...

  5. 【Azure 应用服务】PHP应用部署在App Service for Linux环境中,上传文件大于1MB时,遇见了413 Request Entity Too Large 错误的解决方法

    问题描述 在PHP项目部署在App Service后,上传文件如果大于1MB就会遇见 413 Request Entity Too Large 的问题. 问题解决 目前这个问题,首先需要分析应用所在的 ...

  6. 项目操作案例丨西门子PLC通过网关连接ACS800变频器

    本案例控制对象为炉条机.以及蒸汽的控制以及现场数据参数的显示以及报警. PLC 选用西门子 CPU,通过 ET200 IO 模块控制现场设备并监控数据.变频器采用ABB ACS800变频器,将ABB ...

  7. Java连接数据库从入门到入土

    Java连接数据库 一.最原始的连接数据库 是没有导入任何数据源的:只导入了一个数据库驱动:mysql-connector-java-8.0.27.jar 首先是编写db.proterties文件 d ...

  8. 从零开始Blazor Server(9)--修改Layout

    目前我们的MainLayout还是默认的,这里我们需要修改为BootstrapBlazor的Layout,并且处理一下菜单. 修改MainLayout BootstrapBlazor已经自带了一个La ...

  9. luoguP4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 (线段树-权值-动态开点,树链剖分)

    中学毕业了,十七号就要前往武汉报道.中学的终点是武汉大学,人生的终点却不是,最初的热情依然失却,我还是回来看看这分类排版皆惨淡的博客吧,只是是用来保存代码也好.想要换一个新博客,带着之前的经验能把它整 ...

  10. Docker 08 部署Elasticsearch

    参考源 https://www.bilibili.com/video/BV1og4y1q7M4?spm_id_from=333.999.0.0 https://www.bilibili.com/vid ...