回溯法去重需要先排序

文章介绍了在处理包含重复元素的集合问题时,如何通过排序结合两种方法(一种是排序后逐个检查,另一种是使用unordered_set记录已出现元素)实现树层上的去重。特别强调了排序的重要性,尤其是在不能对输入排序的情况下,如LeetCode的491题。
摘要由CSDN通过智能技术生成

对于集合、组合、排列等经典回溯问题,当所提供元素出现重复时,往往需要去重。

以集合问题为例,根据代码随想录的题解,一般有两种办法进行树层上的去重。

方法一:先排序,从startindex的第二个元素开始,如果和前一个元素相等,则跳过该元素。这里排序是很自然的想法,因为要将相同的元素放在一起,才能在遍历时轻松找到重复元素。代码如下(copy from 代码随想录):

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        result.push_back(path);
        for (int i = startIndex; i < nums.size(); i++) {
            // 而我们要对同一树层使用过的元素进行跳过
            if (i > startIndex && nums[i] == nums[i - 1] ) { // 注意这里使用i > startIndex
                continue;
            }
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }

public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end()); // 去重需要排序
        backtracking(nums, 0);
        return result;
    }
};

方法二:先排序,使用unordered_set记录当前层出现的元素,后续再出现时直接跳过,从而完成树层上的去重。代码如下(copy from 代码随想录):

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        result.push_back(path);
        unordered_set<int> uset;
        for (int i = startIndex; i < nums.size(); i++) {
            if (uset.find(nums[i]) != uset.end()) {
                continue;
            }
            // 记录当前层出现的元素进行去重,注意不要传到下一层去
            // 下一层会要一个新的uset记录出现过的元素
            uset.insert(nums[i]);
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }

public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end()); // 去重需要排序
        backtracking(nums, 0);
        return result;
    }
};

对于方法二,既然已经利用了额外空间记录出现过的元素,那么就可以很容易进行树层上的去重,为什么还要进行排序呢。这是因为去重时需要连续跳过全部重复元素。

举个例子,对[4,4,4,1,4]获取全部子集并去重。如果不排序,那么对于树的第二层(第一层是空集),选取第一个4完成回溯后,直接来到1的位置,而1的后面还有元素4,则会出现[1,4]这个集合,而在之前的回溯过程中,已经出现了[4,1]这个集合了,也就是出现了重复。实际上,不排序得到的结果还有[4,1,4]和[4,4,1]重复,以及[4,4,1,4]和[4,4,4,1]重复。可见原序列中的最后一个4对结果造成了影响,因此需要连续跳过全部重复元素。

当我们排序之后,就变成了对[4,4,4,4,1](或者[1,4,4,4,4])获取全部子集并去重。使用额外的空间记录该层出现过的元素,便能连续跳过全部重复元素,从而完成去重。

而对于另外一道题,491. 非递减子序列 - 力扣(LeetCode),也是要求对结果进行去重,然而不可能对输入进行排序,那要怎么办呢。其实因为输出的子集本身就要求是有序的,那么1后面的4必然会被跳过,那么输出自然就不会同时有[1,4]和[4,1]这种顺序相反的结果了。因此只需要使用额外的集合去记录某层出现过的元素,便能完成去重。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值