关于回溯算法的去重

1,树层去重:

当题目给的集合中有重复的数字,但是要求结果不能相同,此时应该去重

有两种树层去重的方法:

方法一:当不需要考虑原集合的顺序时,可采用排序去重法

如例题:力扣90. 子集 II(JavaScript)_csD_Dscnnnnnnn的博客-CSDN博客

90.子集II :给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

  • 输入: [1,2,2]
  • 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]

集合中有重复的元素,但是组合要求不能重复,需要去重(sort)

    //去重需要排序
    nums.sort()
    .......
    //同一层重复的元素剪枝
    if(i>index&&nums[i]==nums[i-1]){
          continue
     }

方法二:当需要考虑原集合的顺序,不能打乱顺序,则不能使用排序法,可以用标记法

如例题:力扣491. 递增子序列(JavaScript)_csD_Dscnnnnnnn的博客-CSDN博客

491.递增子序列 :给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。

示例:

  • 输入: [4, 6, 7, 7]
  • 输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]

用数组use标记每层元素的使用情况,如果使用了,则需要标记为true,则跳过此次循环

let use=[]
        for(let i=index;i<nums.length;i++){
            if(p.length>0&&nums[i]<p[p.length-1]){
                continue
            }
            //如果没出现过为undefined
            if(use[nums[i]]){
                continue
            }
            //记录出现过的数
            use[nums[i]]=true
            p.push(nums[i])
            dd(i+1)
            p.pop()
        }
 
    }

2,树枝去重:

什么时候需要树枝去重:在全排列问题中,递归到下一层的选择范围总是整个集合,只需要排除掉已经加入path的节点,所以需要采用树枝的去重

树枝去重只有一种方法:标记法

如例题:力扣46. 全排列(JavaScript)_csD_Dscnnnnnnn的博客-CSDN博客

46.全排列:给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

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

因为是树枝去重,涉及递归,所以把use当作参数传递,当回溯的时候也要记得去除标记

for(let i=0;i<nums.length;i++){
            //排除当前path中出现过的值
            if(use[nums[i]]){
                continue
            }
            p.push(nums[i])
            use[nums[i]]=true
            dd(use)
            p.pop()
            use[nums[i]]=false  //回溯时,组合中出现过的值,需要去除标记
        }

3,树层去重和树枝去重的双应用

如例题:力扣47. 全排列 II(JavaScript)_csD_Dscnnnnnnn的博客-CSDN博客

47.全排列 II :给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:

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

分析题目:集合中有重复的元素,且要求结果不能重复,需要树层去重(排序法),因为是全排列问题,每一层的选择范围都是整个集合(除了同一个树枝中使用过的元素),所以需要树枝去重(标记法)

什么时候可以跳过循环呢?

        1,树层的跳出:当前元素和前一个元素相等,且use【i-1】值为false(撤销标记的记录,同层使用过相等的值,因为没有标记过的话,use的值应该为undefined),则continue

        2,树枝去重:在递归中传递use,如果加入path则标记为true,当use为true时,continue

需要注意的是:本次保存在use中的是排序过后的集合的下标

for(let i=0;i<len;i++){
            //数层去重
            if(i>0&&nums[i]==nums[i-1]&&use[i-1]==false){
                continue
            }
            //树枝去重
            if(use[i]){
                continue
            }
            use[i]=true
            p.push(nums[i])
            dd(use)
            p.pop()
            use[i]=false
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值