Subsets

Subsets (leetcodelintcode)

Given a set of distinct integers, return all possible subsets.

Notice
- Elements in a subset must be in non-descending order.
- The solution set must not contain duplicate subsets.

Example
If S = [1,2,3], a solution is:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

Challenge 
Can you do it in both recursively and iteratively?

解题思路

一、DFS

要求一是子集中元素为升序,故先对原数组进行排序。要求二是子集不能重复,于是将原题转化为数学中的组合问题,使用深度优先搜索(DFS)进行穷举求解。

借用参考链接 1 中的解释,具体穷举过程可以用图示和函数运行的堆栈图理解,以数组[1, 2, 3]为例进行分析,下图所示为listresult动态变化的过程,箭头向下表示list.addresult.add操作,箭头向上表示list.remove操作。为了确保所有的情况都能够遍历到,在list加入一个元素后,还需要删除该元素以恢复原状态。

函数dfs(result, list, nums, 0)则表示将以list开头的所有组合全部加入result。当list[1]时,对应图中步骤 2~7 ,依次将[1, 2], [1, 2, 3], [1, 3]全部添加到result中。

算法复杂度
  • 时间复杂度:在本题中解的个数为 2^n ,产生一个解的复杂度最坏可以认为是 n ,故算法时间复杂度的上界可以认为是 O(n* 2^n)
  • 空间复杂度:使用临时空间 list 保存中间结果, list 最大长度为数组长度,故空间复杂度近似为 O(n)
易错点
  1. 记得给原始数组排序,以确保最终子数组是非降的。

Java 实现:

class
Solution
{

/**
     * 
@param
 nums: A set of numbers.
     * 
@return
: A list of lists. All valid subsets.
     */
public
 ArrayList
<
ArrayList
<
Integer
>
>
 subsets(
int
[] nums) {

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

// 注意:此处int型数组取长度无需括号()
if
 (nums == 
null
 || nums.length == 
0
) {
            result.add(
new
 ArrayList
<
Integer
>
());  
// when nums = [], return [[]]
return
 result;
        }
        ArrayList
<
Integer
>
 list = 
new
 ArrayList
<
Integer
>
();
        Arrays.sort(nums);

        dfs(result, list, nums, 
0
);

return
 result;
    }


private
void
dfs
(ArrayList
<
ArrayList
<
Integer
>
>
 result,
                     ArrayList
<
Integer
>
 list, 
int
[] nums, 
int
 pos)
{

        result.add(
new
 ArrayList
<
Integer
>
(list));


for
 (
int
 i = pos; i 
<
 nums.length; i++) {
            list.add(nums[i]);
            dfs(result, list, nums, i + 
1
);
            list.remove(list.size() - 
1
);   
//移除最后一个元素

        }

    }
}

二、Bit Map

一共2^n个子集,每个子集对应0 … 2^n - 1之间的一个二进制整数,该整数一共nbit位,用第ibit位的取值10表示nums[i]在或不在集合中,我们只需遍历完所有的数字——对应所有的bit位可能性(外循环),然后转化为对应的数字集合——判断数字每一个bit的取值(内循环)——即可。

以nums = [1,2,3]举例
0 -
>
 000 -
>
 null                    -
>
 []
1 -
>
 001 -
>
 nums[0]                 -
>
 [1]
2 -
>
 010 -
>
 nums[1]                 -
>
 [2]
3 -
>
 011 -
>
 nums[0],nums[1]         -
>
 [1,2]
4 -
>
 100 -
>
 nums[2]                 -
>
 [3]
5 -
>
 101 -
>
 nums[1],nums[2]         -
>
 [1,3]
6 -
>
 110 -
>
 nums[1],nums[2]         -
>
 [2,3]
7 -
>
 111 -
>
 nums[0],nums[1],nums[2] -
>
 [1,2,3]

Java 实现:

class
Solution
{

/**
     * 
@param
 nums: A set of numbers.
     * 
@return
: A list of lists. All valid subsets.
     */
public
 ArrayList
<
ArrayList
<
Integer
>
>
 subsets(
int
[] nums) {

// write your code here
if
 (nums == 
null
 || nums.length == 
0
) {

return
new
 ArrayList
<
ArrayList
<
Integer
>
>
();
        }

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

int
 n =  nums.length;
        Arrays.sort(nums);  
// 注意函数使用是Arrays.sort()
// 1 
<
<
 n = 100...0 , i的取值范围是0 ~ 2^n -1
// i用来遍历所有的比特位可能性
for
 (
int
 i = 
0
; i 
<
 (
1
<
<
 n); i++) {
            ArrayList
<
Integer
>
 subset = 
new
 ArrayList
<
Integer
>
();

for
 (
int
 j = 
0
; j 
<
 n; j++) {

// j的取值范围是0 ~ n
// j用来判断n个bit位的取值是否为1
if
 ((i 
&
 (
1
<
<
 j)) != 
0
) {
                    subset.add(nums[j]);
                }
            } 
// for j 

            result.add(subset);
        } 
// for i
return
 result;
    }

}

参考

  1. Subsets - 子集 | 数据结构与算法/leetcode/lintcode题解
  2. Subsets | 九章算法
  3. LeetCode: Subsets 解题报告 | Yu's garden

results matching ""

    No results matching ""