- 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] 去重
- 要一直维持同一种 方案:
- 树层去重
- 树枝去重
- 要一直维持同一种 方案:
- 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. 题外话
- 树层上对前一位去重非常彻底,效率很高,树枝上对前一位去重虽然最后可以得到答案,但是做了很多无用搜索