78.M 子集

Problem: 78. 子集

思路

思路1:回溯法

  • 核心思想:通过递归遍历每个元素的 “选” 与 “不选” 状态,生成所有可能的子集组合。

  • 状态定义:

    • path:存储当前递归路径上已选择的元素。

    • start:当前遍历的起始索引,避免重复选择前面的元素(如选了 1 后,后续只能从 2 开始选,避免生成 [1,1])。

  • 递归流程:

    • 记录当前状态:每次进入递归时,先将当前path深拷贝后存入结果集(包括空集)。

    • 选择元素:从start开始遍历数组,依次选择当前元素加入path,并递归处理下一层(起始索引为i+1)。

    • 回溯:递归返回后,移除最后一个元素,尝试其他组合。

    • 时间复杂度:O (n×2ⁿ),生成 2ⁿ个子集,每个子集拷贝需 O (n)。

思路2:位运算枚举法

  • 核心思想:利用二进制掩码(mask)表示元素的选择状态,每一位代表对应索引的元素是否被选中。所有掩码处理完后,得到所有子集。

  • 时间复杂度:O (n×2ⁿ),枚举 2ⁿ个掩码,每个掩码检查 n 位。

Code

代码1:

func subsetsV1(nums []int) [][]int {
	var ans [][]int                // 存储最终的所有子集
	var path []int                 // 存储当前递归路径上的元素
	backtrace(nums, 0, path, &ans) // 从索引 0 开始回溯
	return ans
}

func backtrace(nums []int, start int, path []int, ans *[][]int) {
	// 将当前路径的深拷贝加入结果集(避免后续修改影响已加入的路径)
	*ans = append(*ans, append([]int{}, path...))

	// 从 start 开始遍历剩余元素,避免重复组合
	for i := start; i < len(nums); i++ {
		// 选择当前元素
		path = append(path, nums[i])
		// 递归处理下一个元素(注意这里传入的是 start+1,导致错误)
		backtrace(nums, i+1, path, ans)
		// 回溯:撤销选择,移除最后一个元素,尝试其他可能性
		path = path[0 : len(path)-1]
	}
}

代码2:

func subsets(nums []int) [][]int {
	var ans [][]int
	for mask := 0; mask < 1<<len(nums); mask++ {
		var set []int
		for i := 0; i < len(nums); i++ {
			if mask>>i&1 == 1 {
				set = append(set, nums[i])
			}
		}
		ans = append(ans, set)
	}
	return ans
}

Last updated

Was this helpful?