代码随想录 day29 第七章 回溯算法part05

  • 491.递增子序列
  • 46.全排列
  • 47.全排列 II

1. 递增子序列

关联 leetcode 491.递增子序列

本题和大家刚做过的 90.子集II 非常像,但又很不一样,很容易掉坑里。

  • 思路

    • 不能改变原数组顺序
      • 不能先排序
    • 去重
      • 同一层去重
      • 树枝上可以有重复元素
    • 新元素添加条件
      • 大于等于当前次收集数组最右元素
        • value > array[right]
  • 题解

    func findSubsequences(nums []int) [][]int {
    	rets := make([][]int, 0)
    	ret := make([]int, 0) //从左到右一次递增
    
    	var backtracking func(nums []int, startIdx int)
    	backtracking = func(nums []int, startIdx int) {
    		//append rets
    		if len(ret) > 1 {
    			tmp := make([]int, len(ret))
    			copy(tmp, ret)
    			rets = append(rets, tmp)
    		}
    
    		//the map to record if the value used
    		usedMap := make(map[int]bool)
    		for i := startIdx; i < len(nums); i++ {
    			curVal := nums[i]
    			// 当前层用过了
    			if usedMap[curVal] {
    				continue
    			}
    			// 取到的数 < 最右的数
    			if len(ret) > 0 && curVal < ret[len(ret)-1] {
    				continue
    			}
    			usedMap[curVal] = true
    			ret = append(ret, curVal)
    			backtracking(nums, i+1)
    			ret = ret[:len(ret)-1]
    		}
    	}
    
    	backtracking(nums, 0)
    	return rets
    }
    

2. 全排列

关联 leetcode 46.全排列

  • 思路

    • 排列开始要考虑顺序了
      • 【1,2】和【2,1】是两个结果
      • 处理排列问题就不用 startIndex 了
      • 需要用一个 used 数组来标记元素的使用
        • 全排列,所有元素都要使用到
    • 收割点
      • 叶子节点处
      • 单层循环的 ret 里面收集的元素数量 == nums 包含的元素数量
        • 取完了这次的全排列
  • 题解

    func permute(nums []int) [][]int {
    	rets := make([][]int, 0)
    	ret := make([]int, 0)
    
    	used := make([]bool, len(nums))
    	var backtracking func(nums []int, used []bool)
    	backtracking = func(nums []int, used []bool) {
    		if len(ret) == len(nums) { //完成了该轮全排列收集
    			tmp := make([]int, len(ret))
    			copy(tmp, ret)
    			rets = append(rets, tmp)
    		}
    		for i := 0; i < len(nums); i++ {
    			if used[i] {//该元素已经收集过了, 收集下一个
    				continue
    			}
    			used[i] = true//收集新元素
    			ret = append(ret, nums[i])
    			backtracking(nums, used)//回溯
    			ret = ret[:len(ret)-1]
    			used[i] = false
    		}
    	}
    
    	backtracking(nums, used)
    	return rets
    }
    

3. 全排列 II

关联 leetcode 47.全排列 II

  • 思路

    • 在上一道题的基础上,增加去重
      • 同一层去重
        • used[i-1]==false
        • 在一个 for 里面的元素去重
      • 树枝去重
        • used[i-1] == true
        • 会多出一些树枝衍生无用操作
    • 一定要加上 used[i - 1] == false或者used[i - 1] == true,因为 used[i - 1] 要一直是 true 或者一直是false 才可以,而不是 一会是true 一会又是false。 所以这个条件要写上
      • used[i-1] 去重
        • 要一直维持同一种 方案:
          • 树层去重
          • 树枝去重
  • 题解

    func permuteUnique(nums []int) [][]int {
    	rets := make([][]int, 0)
    	ret := make([]int, 0)
    	used := make([]bool, len(nums))
    	sort.Ints(nums)
        
    	var backtracking func(nums []int, curLen int)
    	backtracking = func(nums []int, curLen int) {
    		if curLen == len(nums) { //完成了该轮全排列收集
    			tmp := make([]int, len(ret))
    			copy(tmp, ret)
    			rets = append(rets, tmp)
    		}
    		for i := 0; i < len(nums); i++ {
    			// used[i - 1] == true,说明同一树枝nums[i - 1]使用过
          // used[i - 1] == false,说明同一树层nums[i - 1]使用过
    			// 一定要 used[i-1] 这个判断
    			if i > 0 && nums[i] == nums[i-1] && !used[i-1] {
    				continue
    			}
    			if !used[i] { //该元素已经收集过了, 收集下一个
    
    				used[i] = true //收集新元素
    				ret = append(ret, nums[i])
    				backtracking(nums, curLen+1) //回溯
    				ret = ret[:len(ret)-1]
    				used[i] = false
    			}
    		}
    	}
    
    	backtracking(nums, 0)
    	return rets
    }
    

    4. 题外话

    • 树层上对前一位去重非常彻底,效率很高,树枝上对前一位去重虽然最后可以得到答案,但是做了很多无用搜索

你可能感兴趣的:(代码随想录,算法,golang,剪枝)