前端算法面试必刷题系列[17]

490 阅读3分钟

这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。

30. 全排列II (permutations-ii)

标签

  • DFS
  • 回溯
  • 中等

题目

leetcode 传送门

这里不贴题了,leetcode打开就行,题目大意:

给定一个可包含重复数字的序列,返回所有不重复的全排列。

相关知识

再加深点回溯的印象

google: 回溯法(back tracking)(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。 但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

wiki: 回溯法(英语:backtracking)是暴力搜索法中的一种。 对于某些计算问题而言,回溯法是一种可以找出所有(或一部分)解的一般性算法,尤其适用于约束补偿问题(在解决约束满足问题时,我们逐步构造更多的候选解,并且在确定某一部分候选解不可能补全成正确解之后放弃继续搜索这个部分候选解本身及其可以拓展出的子候选解,转而测试其他的部分候选解)。

dfs + 回溯算法的要素:

  1. 为解空间 定义 递归函数,搜索解空间(通常是DFS)

  2. 确定有效结果的方式

    • 这题是 curPath.length === len就找到了一个有效解放进 res
  3. 回溯范围

    • 仍然是全部遍历,因为每一层都要考虑全部元素
  4. 剪枝条件

    • 找出所有不符合条件进行舍弃,本题剪枝条件是下面
    • (1)curUsed[i] === true 表示在本层回溯中已经用过,直接舍弃(一轮就表示确定了路径上的一位数)
    • (2)i > 0 && nums[i] === nums[i-1] && curUsed[i-1] === false 由于已经排好序了,所以相同元素相邻。那么搜索时当前值等于前一个值会产生两种情况:1) nums[i-1] 没用过,说明回溯到了同一层,此时接着用num[i]则会与同层用num[i-1]重复 2) nums[i-1] 用过了 说明此时在num[i-1]的下一层那么相等就不会重复

总结其核心思想是递归结束条件剪枝条件和止归时路径的回退操作

总结模板

var dfsProblems = function(s) {
    let res = []
    let dfs = (curPath, start(可选参数)) => {
        if (符合条件) { 
            res.push(xFunction(curPath))
        }
        if (剪枝条件) return
        // 下面是根据题意 有几个岔路,开始选择
        for() {
            curPath.push(xxx)
            dfs(curPath, start(可选参数)) // 递归深搜
            // 回溯
            curPath.pop()
        }
    }
    dfs([], start(可选参数))
    return res
}

基本思路

全排列相同思路

写法实现

var permuteUnique = function(nums) {
  let [res, len, curUsed] = [[], nums.length, {}]
  nums = nums.sort((a, b) => a - b)
  let dfs = (curPath) => {
    if (curPath.length === len) {
      res.push(curPath.slice())
      return
    }
    for (let i = 0; i < len; i++) {
      if (curUsed[i]) {
        continue;
      }
      // 当前值用过了 或 
      // 当前值等于前一个值: 两种情况:
      // 1 nums[i-1] 没用过 说明回溯到了同一层 此时接着用num[i] 则会与 同层用num[i-1] 重复
      // 2 nums[i-1] 用过了 说明此时在num[i-1]的下一层 相等不会重复
      if (i > 0 && nums[i] === nums[i-1] && curUsed[i-1] === false) {
        continue
      }
      curPath.push(nums[i])
      curUsed[i] = true
      dfs(curPath)
      curPath.pop()
      curUsed[i] = false
    }
  }
  dfs([])
  return res
};

console.log(permuteUnique([1,1,2]))

另外向大家着重推荐下这位大哥的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the rever monster,我看到就通过,暗号对不上不加哈,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧