【算法解析】子集问题 —— DFS 递归解法详解

下面给出一篇详细解析该题 DFS 解法的博客文章,希望能帮助大家更好地理解这个算法的思路。


【算法解析】子集问题 —— DFS 递归解法详解

在本篇文章中,我们将讨论如何利用深度优先搜索(DFS)来生成一个数组的所有子集(即幂集)。题目要求给定一个互不相同的整数数组 nums,返回该数组所有可能的子集。

问题描述

给定一个数组 nums,其中的所有元素都互不相同。要求返回 nums 的所有可能子集,注意解集不能包含重复的子集,且可以按任意顺序返回。
例如:

  • 输入:nums = [1,2,3]
  • 输出:[[], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]]

算法思路

本题可以采用 DFS 的递归方法来遍历所有可能的子集。具体来说,我们可以使用回溯算法,在递归的过程中维护一个「路径」(即当前选取的子集元素),当遍历到数组末尾时,将当前路径加入答案集合中。每一层递归都有两种选择:选取当前数字或者跳过当前数字

代码中对数组进行了排序,其实这一步主要在有重复数字的情况下有用(通过跳过重复数字可以避免重复解),但对于本题中“互不相同”的数组来说,排序并不是必需的。不过为了通用性以及代码的一致性,这里依然进行了排序。

代码实现

下面是代码实现(Python 版本):

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        # 排序(主要是为处理重复元素,如果已知元素互不相同,这步可以省略)
        nums.sort()
        path = []   # 当前子集
        ans = []    # 存储所有子集
        n = len(nums)

        def dfs(i: int) -> None:
            # 当索引越界,说明已经考虑完所有数字,将当前路径加入答案中
            if i == n:
                ans.append(path.copy())
                return 

            # 分支1:选择当前数字
            x = nums[i]
            path.append(x)
            dfs(i + 1)
            path.pop()

            # 分支2:不选择当前数字(跳过所有与当前数字相同的元素)
            # 因为本题数字互不相同,这里的 while 循环其实不会跳过多个元素
            i = i + 1
            while i < n and nums[i] == x:
                i += 1
            dfs(i)

        # 从索引0开始搜索所有子集
        dfs(0)
        return ans

代码解析

1. 排序与初始化

nums.sort()
path = []   # 用于记录当前选择的子集
ans = []    # 用于存放所有生成的子集
n = len(nums)

对数组进行排序可以方便后续跳过重复数字的操作。这里初始化了两个列表:

  • path:用于存储当前递归路径上选取的数字,也就是当前生成的子集;
  • ans:最终存储所有满足条件的子集。

2. 递归函数 dfs

递归函数 dfs(i: int) 中的参数 i 表示当前处理到数组的第 i 个元素。

终止条件
if i == n:
    ans.append(path.copy())
    return 

当索引 i 到达数组末尾时,说明当前递归路径已经处理完所有数字,将当前路径(子集)加入答案 ans 中。

分支1:选择当前数字
x = nums[i]
path.append(x)
dfs(i + 1)
path.pop()

首先选择当前数字 x 并将其加入路径中,然后递归调用 dfs(i + 1) 考虑后续数字。递归结束后通过 path.pop() 回溯,撤销之前的选择,保证路径状态正确。

分支2:不选择当前数字
i = i + 1
while i < n and nums[i] == x:
    i += 1
dfs(i)

在回溯后,不选择当前数字 x。这里先将 i 增加1,然后利用 while 循环跳过所有与 x 相同的数字。虽然题目保证数字互不相同,但这个逻辑可以应对有重复数字的情况,从而避免生成重复子集。最后,递归调用 dfs(i) 继续搜索后续数字。

3. 开始递归搜索

最后,通过调用 dfs(0) 从索引 0 开始搜索所有子集,并返回答案 ans

总结

本题利用 DFS 递归和回溯的方法,枚举了所有可能的子集。主要思路就是对每个数字都有「选」和「不选」两种选择,通过递归来构造出所有可能的组合。对于处理重复数字的情况,通过在「不选」分支中跳过相邻重复数字可以避免生成重复子集。尽管本题中数字互不相同,但这种写法具有更好的通用性。

希望这篇文章能够帮助你深入理解 DFS 在子集问题中的应用!如果你有任何疑问或建议,欢迎在评论区讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值