js算法题解(第29天) ---- leetcode 17. 电话号码的字母组合

199 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

前言

每天一道算法题,死磕算法

题目

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

image.png

示例 1:

输入: digits = "23"
输出: ["ad","ae","af","bd","be","bf","cd","ce","cf"]

分析

看到这道题我们应该立马想到这是一道排列组合的题目

排列组合题目我们应该第一时间想到回溯算法,这一定要建立起条件反射,不明白为什么排列组合要第一时间想到回溯算法的同学,可以看我的另一篇文章js算法题解(第二十天)---leetcode-46. 全排列

遇到这种问题,我们都有模板的,在复习一下就可以啦
先搞懂三个概念

  1. 路径(track):也就是当前已经做出的选择
  2. 选择列表(nums):也就是当前可以做的选择
  3. 结束条件: 也就是结束选择需要判断的条件
result = [];
let backTrack = function(nums,track){
    if(满足条件){
        result.push(track);
        return;
    }
    for(选择 in nums){
        添加到路径中
        backTrack(nums,track)
        从路径中撤销    
    }
}

所以我们就想办发让我们的题目套用我们的模板


/**
 * 
 * @param {string} track 当前路径,因为路径都是字符串,所以track应该是字符串类型
 * @param {string} nums  可选择列表,可选择列表也就是题目给出的数字,所以也是字符串
 * @param {number} deep  当前深度,也就是当前选择到选择列表的第几个了
 */
var backTrack = function(track,nums,deep){
  // 判断结束条件
  if(track.length===nums.length){
    result.push(track);
    return;
  }
  // 解析可选择列表
  let chart = nums[deep];
  let words = digitsToWord[chart];

  for(let i=0;i<words.length;i++){
    // 添加到路径中
    track+=words[i];
    backTrack(track,nums,deep+1);
    // 回溯,删除最后一个字符
    track=track.substring(0,track.length-1);
  }

}

题解


// 要返回得结果
let result = [];
// 首先定义一个号码对应字母的映射对象
const digitsToWord = {
  2:'abc',
  3:'def',
  4:'ghi',
  5:'jkl',
  6:'mno',
  7:'pqrs',
  8:'tuv',
  9:'wxyz',
}
var letterCombinations = function(digits) {
  // 判断字符串是否为空
  if(!digits){
    return result;
  }
  
  // 要弄清路径和可选择列表
  backTrack('',digits,0)
  return result;
};
/**
 * 
 * @param {*} track 当前路径
 * @param {*} nums  可选择列表
 * @param {*} deep  当前深度
 */
var backTrack = function(track,nums,deep){
  // 判断结束条件
  if(track.length===nums.length){
    result.push(track);
    return;
  }
  // 解析可选择列表
  let chart = nums[deep];
  let words = digitsToWord[chart];

  for(let i=0;i<words.length;i++){
    track+=words[i];

    backTrack(track,nums,deep+1);
    track=track.substring(0,track.length-1);
  }

}

但是你会发现这样写在leetcode是不能通过的,因为

力扣的判题机在读取您的代码后,对每个测试用例,都会初始化一次类,但全局变量和类内静态变量需要您手动初始化。

所以我们的题解中应该避免出现全局变量和静态变量

所以符合leetcode上的答案是

var letterCombinations = function(digits) {
  // 要返回得结果
  let result = [];
  // 判断字符串是否为空
  if(!digits){
    return result;
  }
 
  
  // 要弄清路径和可选择列表
  backTrack('',digits,0,result)
  return result;
};
/**
 * 
 * @param {*} track 当前路径
 * @param {*} nums  可选择列表
 * @param {*} deep  当前深度
 */
var backTrack = function(track,nums,deep,result){
  // 首先定义一个号码对应字母的映射对象
  const digitsToWord = {
    2:'abc',
    3:'def',
    4:'ghi',
    5:'jkl',
    6:'mno',
    7:'pqrs',
    8:'tuv',
    9:'wxyz',
  }
  // 判断结束条件
  if(track.length===nums.length){
    result.push(track);
    return;
  }
  // 解析可选择列表
  let chart = nums[deep];
  let words = digitsToWord[chart];

  for(let i=0;i<words.length;i++){
    track+=words[i];

    backTrack(track,nums,deep+1,result);
    track=track.substring(0,track.length-1);
  }

}

总结

模板大法好,遇到了难题后,要多总结模板

遇到题型,先辨别属于哪一类题目,然后再套用模板,效率会快很多

参考