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

525 阅读4分钟

封面选的好,点赞少不了。^ - ^

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

10. 电话号码的字母组合 (letter-combinations-of-a-phone-number)

标签

  • DFS/BFS
  • 回溯
  • 中等

题目

leetcode 传送门

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

给出数字到字母的电话映射。再给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。其实就是一个深度遍历问题。

基本知识

我们先简单了解 DFS / BFS 到时候查找算法会精说,先留个印象。

DFS(Deep First Search)深度优先搜索。

深度优先搜索的步骤分为

  1. 递归下去
  2. 回溯上来
  • 顾名思义,深度优先,先一条路走到底,直到达到目标。这里称之为递归下去
  • 否则既没有达到目标又无路可走了,那么则退回到上一步的状态走其他路。这便是回溯上来

BFS(Breath First Search)广度优先搜索。

广度优先搜索较之深度优先搜索之不同在于,深度优先搜索旨在不管有多少条岔路,先一条路走到底,不成功就返回上一个路口然后就选择下一条岔路。

而广度优先搜索旨在面临一个路口时,把所有的岔路口都记下来,然后选择其中一个进入,然后将它的分路情况记录下来,然后再返回来进入另外一个岔路,并重复这样的操作

两者不同

  1. 数据结构上的运用 这个结合下面代码理解
  • DFS用递归的形式,用到了结构,先进后出
  • BFS选取状态用队列的形式,先进先出
  1. 复杂度

DFS的复杂度与BFS的复杂度大体一致,不同之处在于遍历的方式与对于问题的解决出发点不同,

  • DFS适合目标明确
  • BFS适合大范围的寻找
  1. 思想 思想上来说这两种方法都是遍历穷举所有的情况

基本思路

  1. 建立一个映射 map ,用来映射数字=>字母串
  2. 使用 dfs 来递归地遍历出结果

写法实现

/**
 * @param {string} digits
 * @return {string[]}
 */
const letterMap = {
  '2': 'abc',
  '3': 'def',
  '4': 'ghi',
  '5': 'jkl',
  '6': 'mno',
  '7': 'pqrs',
  '8': 'tuv',
  '9': 'wxyz'
}

var letterCombinations = function(digits) {
  let res = []
  // DFS 的使用,传入参数就是 digits数字串,
  // index表示递归到数字串的位数,
  // tempStr是用来拼接的临时子串
  let letterFun = (digits, index, tempStr) => {
    // 递归出口,当我们的 index 已经是 digits 长度表示已经到最后一位了
    // 那么 tempStr 就是结果之一了,直接把这个子串入结果数组
    if (index === digits.length) {
      res.push(tempStr)
      return;
    }
    // 我们也用个临时变量清楚些表示下当前我们准备拼接的数字映射的字母串
    let letters = letterMap[digits[index]]
    for (letter of letters) {
      // 重点就是递归地使用每一个字母拼接到子串后面到下一轮递归
      letterFun(digits, index + 1, tempStr + letter)
    }
    return
  }
  // 特殊判定:空直接输出 []
  if (digits === "") {
      return []
  }
  // 使用 DFS 递归下去,回溯上来
  letterFun(digits, 0, '')
  return res
};

console.log(letterCombinations('23'))

其他方式,不用递归,用迭代的函数式写法来一个

var letterCombinations = function (digits) {
  // 用这个数组 index 来当key 把些子串转数组
  const CHAR_MAP = ['', '', 'abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz'].map((s) => s.split(''));
  return digits.length === 0 ? [] : 
    digits.split('')
    .map((d) => CHAR_MAP[d])
    .reduce((acc, cur) => acc.reduce(
        (iacc, parent) => iacc.concat(
          cur.map((item) => `${parent}${item}`)
        ), []) // 注意这是有初值的,所以 cur 从第一个元素开始
    ); // 这是有初值的,所以 cur 从第二个元素开始
};

今天是年三十,就来一题吧。

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the reiver monster,我看到就通过,暗号对不上不加哈

参考