LeetCode78 子集(带扩展)

leetcode.cn/problems/su...

解法一:回溯

因为集合中的元素不用考虑顺序,我们通过保证元素之间的相对顺序不变来防止出现重复的子集

例如,[1,2,3]2 后面只有 3,如果你添加了前面的 1,那么后面就只能选择23,若选择 了2,后面就只能选择3,否则,如果出现[2,1],就会和之前已经生成的子集 [1,2] 重复了。

得到下面这棵决策树

这棵树的特性是:如果把根节点作为第 0 层,那么第 n 层的所有节点就是大小为 n 的所有子集

题意要求得到所有子集,那只要遍历这棵多叉树,把所有节点的值加入结果即可

go 复制代码
func subsets(nums []int) [][]int {
	path := make([]int, 0)
	var res [][]int
	backtrack(nums, 0, path, &res)
	return res
}

func backtrack(nums []int, offset int, path []int, res *[][]int) {
	if len(path) <= len(nums) { // 每个节点的值都是一个子集
		tmp := make([]int, len(path))
		copy(tmp, path)
		*res = append(*res, tmp)
	}
	for i := offset; i < len(nums); i++ { // 递归退出条件: offset == len(nums)
		path = append(path, nums[i]) // 做选择
		backtrack(nums, i+1, path, res) // 通过offset参数控制树枝的遍历,每次只能往后选择元素,避免产生重复的子集
		path = path[:len(path)-1] // 撤销选择
	}
}

扩展

组合问题和子集问题其实是等价的 ,给定一个数组,大小为 k 的组合就是大小为 k 的子集。

来看这道题:leetcode.cn/problems/co...

回想刚才的决策树,其实这个题就是找出第k层的所有节点,前面的子集代码稍作改动即可

go 复制代码
func combine(n int, k int) [][]int {
    nums := make([]int, 0, n)
    for i:=1; i<=n; i++{
        nums = append(nums, i)
    }
    path := make([]int, 0)
    var res [][]int 
    backtrack(nums, 0, path, k, &res)
    return res
}

func backtrack(nums []int, offset int, path []int, k int, res *[][]int){
    if len(path) == k{ // 收集长度为k的子集
        tmp := make([]int, len(path))
        copy(tmp, path)
        *res = append(*res, tmp)
        return
    }
    for i:= offset; i<len(nums); i++{ // 通过offset避免重复选择元素
        path = append(path, nums[i]) // 做选择
        backtrack(nums, i+1, path, k, res)
        path = path[:len(path)-1] // 撤销选择
    }
}

可以再优化一下空间复杂度,不需要生成一个额外的数组nums

go 复制代码
func combine(n int, k int) [][]int {
    path := make([]int, 0)
    var res [][]int
    backtrack(n, 1, path,k, &res) // 范围是【1,n】
    return res
}

func backtrack(n int, offset int, path []int, k int, res *[][]int) {
    if len(path) == k{
        *res = append(*res, append([]int{}, path...))
        return
    }
    for i:=offset; i<=n; i++{
        path = append(path, i)
        backtrack(n, i+1, path, k, res)
        path = path[:len(path)-1]
    }
}
相关推荐
星浩AI8 分钟前
Google 官方发布:让你的 AI 编程助手"边写、边看、边调",像人类开发者一样工作
人工智能·后端·开源
懒猫爱上鱼11 分钟前
Android 14 中 AMS 对进程优先级的完整管控机制
面试
喵了个Code30 分钟前
Spring Boot 3 + Spring Security + OAuth2 + Gateway企业级认证授权平台实现
后端
开心猴爷36 分钟前
除了 Perfdog,如何在 Windows 环境中完成 iOS App 的性能测试工作
后端
桦说编程1 小时前
简单方法实现子任务耗时统计
java·后端·监控
盖世英雄酱581362 小时前
物品超领取损失1万事故复盘(一)
java·后端
凌览2 小时前
别再死磕 Nginx!http-proxy-middleware 低配置起飞
前端·后端
拾玖不会code2 小时前
简单分表场景下的业务发散思考:分表如何保证丝滑?
后端
CryptoRzz2 小时前
印度尼西亚(IDX)股票数据对接开发
java·后端·websocket·web3·区块链
咕白m6252 小时前
通过 C# 快速生成二维码 (QR code)
后端·.net