LeetCode算法题分享---DFS典型题目

题目链接: LC 90. 子集 II

题目:
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的
子集(幂集)。

要求 : 解集不能包含重复的子集。返回的解集中,子集可以按任意顺序排列。

示例 1:

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

提示

. 1 <= nums.length <= 10
. -10 <= nums[i] <= 10

基本思路分析

  1. 其实就是一个排列组合题目, 例如上面例子中的集合为 1, 2, 2, 其中1的个数为1, 2的个数为2, 故我们进行一个子集选择的时候,1的可能选的个数为0或者1, 一共2种可能, 2 可能选择的个数为 0 个, 1个,2个, 共有三种可能。 故总共的选择根据乘法原理为 2 * 3 = 6 一共六种选择,不过我们题目要求的不是子集的个数,而是子集的各种情况。

  2. 故通过上面的分析,我们知道我们需要保存下nums数组中每个元素的个数,即记录下每个数字的选择方案,是选择0个,还是选择1个,还是X个 。以上面的nums = [1,2,2] 为例子,其具体的过程我们可以用递归搜索树表示如下:
    在这里插入图片描述

  3. 这是一一个典型的深度优先遍历(DFS), 实现的时候,我们用递归进行实现,注意的问题为现场恢复和递归终止条件的判断,看数据的范围数组最多为10个整数,且整数的值范围为 -10 ~ 10 , 故这里的话我们可以直接遍历所有值从 -10 ~ 10 的所有组合,因为如果nums中没有某个值的话,那么它就是0个并不会影响我们上面的递归搜索树, 故下面是具体的代码实现.

class Solution 
{
public:
    unordered_map<int, int> cnt;    // 记录我们nums中的数字具体出现的次数. 其实这里用个普通的数组存储也没毛病.
    vector<vector<int>> res;        // 记录我们最后结果子集的所有情况
    vector<int> path;               // 记录我们每一个子集 

    void dfs(int u)
    {
        if(u > 10) res.push_back(path);     // 若大于10,说明从-10到10所有情况都取了一遍,此时可以作为一个子集加入res中
        else
        {
            for(int i = 0; i < cnt[u] + 1; ++i)  // 遍历的第1遍,说明此时只有0个u在path中
            {
                dfs(u + 1);      //第2遍才加入了一个u, 因为要等dfs(u + 1)递归完第一遍才
                path.push_back(u);   
            }
            for(int i = 0; i < cnt[u] + 1; ++i) path.pop_back();  // 现场恢复,每次遍历完一个分支要将现场恢复到上一个分支.结合上面的递归搜索树理解
        }
    }

    vector<vector<int>> subsetsWithDup(vector<int>& nums) 
    {
        for(auto val : nums) cnt[val]++;     // 遍历一遍 nums, 记录其中每个数字出现的数字
        dfs(-10);                            // 从-10开始遍历 
        return res;                       
    }
};

其他dfs相关题目链接
AC AcWing 842. 排列数字
AC 93. 递归实现组合型枚举
AC 843. n-皇后问题

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值