1、描述
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集
例:输入:nums = [1, 2, 3]
输出:[ [3], [1], [2], [1, 2, 3], [1, 3], [2, 3], [1, 2], [] ]
2、算法
1)二进制位
思想:集合的每个元素,都有可以选或不选,用二进制和位运算,可以很好的表示
func subsets(_ nums: [Int]) -> [[Int]] {
var res : [[Int]] = [[Int]]()
for i in 0..<(1<<nums.count) {
var sub = [Int]()
for j in 0..<nums.count{
if ((i>>j)&1) == 1 {
sub.append(nums[j])
}
}
res.append(sub)
}
return res
}
2)枚举
解法一:逐个枚举
思想:逐个枚举,空集的幂集只有空集,每增加一个元素,让之前幂集中的每个集合,追加这个元素,就是新增的子集
func subsets(_ nums: [Int]) -> [[Int]] {
//循环枚举
var res : [[Int]] = [[Int]]()
res.append([])
for n in nums {
let size = res.count
var i = 0
while i<size{
var sub = [Int](res[i])
sub.append(n)
res.append(sub)
i += 1
}
}
return res
}
解法二:递归枚举
func subsets(_ nums: [Int]) -> [[Int]] {
/*
递归枚举
*/
var res : [[Int]] = [[Int]]()
res.append([])
recursion(nums, 0, &res)
return res
}
private func recursion(_ nums : [Int], _ i : Int, _ res : inout [[Int]]){
if i >= nums.count{
return
}
let size = res.count
var j = 0
while j < size{
var sub = [Int](res[j])
sub.append(nums[i])
res.append(sub)
j += 1
}
recursion(nums, i+1, &res)
}
3)迭代
解法一
思想:解法一的迭代法,是直接从结果上进行分类,将子数组的长度分为长度是 1 的,2 的 .... n 的。
想找出数组长度 1 的所有解,然后再在长度为 1 的所有解上加 1 个数字变成长度为 2 的所有解,同样的直到 n。
示意图:
第一次循环: [1] [2] [3]
第二次循环:[1, 2] [1, 3] [2, 3]
第三次循环:[1, 2, 3]
func subsets(_ nums: [Int]) -> [[Int]] {
/*
迭代一
*/
var res : [[Int]] = [[Int]]()
var ans : [[Int]] = [[Int]]()
ans.append([])
res.append([])
let n = nums.count
//第一层循环,子数组长度从1到n
for i in 1...n {
//第二层循环,遍历上次的所有结果
var tmp : [[Int]] = [[Int]]()
for list in res {
//第三次循环,对每个结果进行扩展
for m in 0..<n {
//只添加比末尾数字答的数字,防止重复
if list.count>0 && list[list.count-1]>=nums[m] {
continue
}
var newList = [Int](list)
newList.append(nums[m])
tmp.append(newList)
res.append(newList)
}
}
ans=tmp
}
return res
}
解法二
思想:我们还可以从条件上入手,先只考虑给定数组的 1 个元素的所有子数组,然后再考虑数组的 2 个元素的所有子数组 ... 最后再考虑数组的 n 个元素的所有子数组。求 k 个元素的所有子数组,只需要在 k - 1 个元素的所有子数组里边加上 nums [ k ] 即可。
图示:
[] --> 初始化空
[] [1] --> [1]
[] [1] [2] [1,2] --> [1,2]
[] [1] [2] [1, 2] [3] [1,3] [2,3] [1,2,3] --> [1,2,3]
func subsets(_ nums: [Int]) -> [[Int]] {
/*
迭代二
*/
var res : [[Int]] = [[Int]]()
res.append([])//初始化空数组
for i in 0..<nums.count {
var res_tmp = [[Int]]()
//遍历之前的所有结果
for list in res {
var tmp = [Int](list)
//加入新增数字
tmp.append(nums[i])
res_tmp.append(tmp)
}
res.append(contentsOf: res_tmp)
}
return res
}
4)回溯
思想:集合中每个元素的选和不选,构成了一个满二叉状态树,比如,左子树是不选,右子树是选,从根节点、到叶子节点的所有路径,构成了所有子集。通过回溯,跳过一些节点
func subsets(_ nums: [Int]) -> [[Int]] {
var res : [[Int]] = [[Int]]()
var sub : [Int] = [Int]()
backtrace(nums, 0, &sub, &res)
return res
}
private func backtrace(_ nums : [Int], _ i : Int, _ sub : inout [Int], _ res : inout [[Int]]){
res.append(sub)
var j = i
while j < nums.count {
sub.append(nums[j])
backtrace(nums, j+1, &sub, &res)
sub.remove(at: sub.count-1)
j += 1
}
}