LeetCode 17. 电话号码的字母组合

题目来源于 LeetCode 的第 17 题,难度为:中等。目前的通过率是56.8%。

  给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按任意顺序返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

图片

图片

  很多排列组合相关的问题,都可以通过(Depth First Search)dfs,深度优先搜索来解决。

解题思路分析:
为了便于理解解题思路,我选取[2,5,8]为例来分析。[2,5,8]对应的字母分别是:[ "abc", "jkl", "tuv" ]

图片

算法步骤:

  1. 第一步选择a

  2. 第二步选择j

  3. 第三步选择t

  4. 然后j->t的这根红色的线回退到j

  5. 然后选择u

  6. 然后j->u的这根线回退到j

  7. 然后选择v

  8. 然后aj切换到ak

  9. ...

  10. 然后ak切换到al

  11. 完成以a开始的组合

  12. ...

  13. 到最后以c开始的组合

核心是: 如何回退?

比如ajt如何回退到aj。从压栈的情景上考虑,我们很容易想到的就是递归。

先看下代码,然后我再根据代码来进行分析,这样会更加容易的理解。

代码如下:

 class LeetsArray: NSObject {
    private let lettersArray: [[Character]] = [ ["a","b","c"], ["d","e","f"], ["g","h","i"], ["j","k","l"], ["m","n","o"], ["p","q","r", "s"], ["t","u","v"], ["w","x","y","z"] ]
    private var digitsArray = [Int]()
    private var list = [String]()
    private var str = [Character]()
    func letterCombinations(_ digits: String) -> [String] {
        if digits.count == 0 { return list }
        digitsArray = Array(digits).map { Int(String($0)) ?? 2 }
        str = [Character](repeating: Character("^"), count: digitsArray.count)
        dfs(index: 0)
        return list
    }
    
    private func dfs(index: Int) {
        if index == digitsArray.count {
            list.append(String(str))
            return
        }
        
        let letters = lettersArray[ digitsArray[index] - 2 ]
        for letter in letters {
            str[index] = letter
            dfs(index: index + 1)
        }
    }
}

代码说明:

  1. lettersArray就是数字的映射表;

    digitsArray是用来存将字符串digits转成数组的元素,比如传入"258",我们会将之存储为[ 2, 5, 8]

    str是用来存储临时生成的字符串如ajt;

    list是用来存我们组合的字符串,比如list = [ [ajt] ,[aju] ,[ajv] ]

  2. func letterCombinations(_ digits: String) -> [String]这个主方法主要是做了变量的初始化,以及如果传进来的digits为空的判空操作。然后调用dfs(index: 0)

  3. func dfs(index: Int)的实现说明 

    1. 如果搜索的深度和传进来的"258".length = 3的深度相等,就将str存入到list里面

    2. 如果深度没有超过就继续执行
      2.1 index==[0,1,2]时分别取出abcjkltuv 
      2.2 然后for循环将值存入str
      2.3 递归调用: dfs(index: index + 1)


  其实看到这里,也真的不好理解。因为有了递归,事情就不仅仅是用简单的语言就能讲述清楚了。为了让大家更好的理解,我就用伪代码来表达递归的思想。以及举一个小的例子来解释这道题是如何回退的。


  根据今天的例子[2,5,8]来讲解,也就是说只有3层,所以我将实现代码整理成了伪代码,如下图:其中红框绿框蓝框框的代码就是三次递归调用的过程。

图片

例子流程详细中间过程:

  从func letterCombinations中的 { dfs(index: 0) }

  1. 这个时候letters = abc,然后进入第9行代码也就是for循环流程

  2. 执行第10行,str[0] = "a",然后执行第11行dfs方法

  3. 进入第一次递归调用此时index = 1,if判断为false,然后取出letters,此时letters = "jkl",然后进入for循环流程,执行到第20行,str[1] = "j",然后执行第21行dfs方法

  4. 进入第二次递归调用此时index = 2,if判断为false,然后取出letters,此时letters = "tuv",然后进入for循环流程,执行到第20行,str[2] = "t",然后执行第31行dfs方法

  5. 进入第三次递归调用此时index = 3,if判断为true,执行list.append(str),然后return,【此时str = "ajt"】。

  6. index == 3后,递归return,就会return到上一步,也就是第4步的for循环中,此时的letters = "tuv",for循环继续,此时的letter改为u。然后执行第31行的dfs

  7. 再次进入递归,此时index == 3,走和第6步一样的逻辑,直到第4步的for循环结束掉,再次return第3步的递归

  8. 一直这样下去,直到遍历完所有。

  详细的过程我通过伪代码和举例子的方式来详细的说明了。大家可以根据上述的伪代码思路走一遍,应该可以很快的理解。

总结:

  今天主要通过这个排列组合的题目,引出了dfs深度优先搜索算法,然后通过使用dfs来解决了这道leetcode的算法题。因为该算法包含递归,为了便于大家理解,我通过伪代码和大家详细的介绍了其实现流程也就是如何回退的,其实站到高一点想想,递归本身就是先疯狂压栈然后再出栈的,所以有一个天然回退的特性。那今天就分享到这啦,希望能帮助到大家。

欢迎关注【无量测试之道】公众号,回复【领取资源】
Python编程学习资源干货、
Python+Appium框架APP的UI自动化、
Python+Selenium框架Web的UI自动化、
Python+Unittest框架API自动化、
资源和代码 免费送啦~
文章下方有公众号二维码,可直接微信扫一扫关注即可。

备注:我的个人公众号已正式开通,致力于测试技术的分享,包含:大数据测试、功能测试,测试开发,API接口自动化、测试运维、UI自动化测试等,微信搜索公众号:“无量测试之道”,或扫描下方二维码:

在这里插入图片描述
添加关注,让我们一起共同成长!  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wu_Candy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值