给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按任意顺序返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
很多排列组合相关的问题,都可以通过(Depth First Search)dfs,深度优先搜索来解决。
解题思路分析:
为了便于理解解题思路,我选取[2,5,8]
为例来分析。[2,5,8]
对应的字母分别是:[ "abc", "jkl", "tuv" ]
。
算法步骤:
a
j
t
j->t
的这根红色的线回退到j
u
j->u
的这根线回退到j
v
aj
切换到ak
ak
切换到al
a
开始的组合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)
}
}
}
代码说明:
lettersArray
就是数字的映射表;
digitsArray
是用来存将字符串digits
转成数组的元素,比如传入"258"
,我们会将之存储为[ 2, 5, 8]
;
str
是用来存储临时生成的字符串如ajt
;
list
是用来存我们组合的字符串,比如list = [ [ajt] ,[aju] ,[ajv] ]
。
func letterCombinations(_ digits: String) -> [String]
这个主方法主要是做了变量的初始化,以及如果传进来的digits
为空的判空操作。然后调用dfs(index: 0)
func dfs(index: Int)
的实现说明 "258".length = 3
的深度相等,就将str存入到list里面abc
、jkl
、tuv
2.2 然后for循环将值存入str
2.3 递归调用: dfs(index: index + 1)
其实看到这里,也真的不好理解。因为有了递归,事情就不仅仅是用简单的语言就能讲述清楚了。为了让大家更好的理解,我就用伪代码来表达递归的思想。以及举一个小的例子来解释这道题是如何回退的。
根据今天的例子[2,5,8]来讲解,也就是说只有3层,所以我将实现代码整理成了伪代码,如下图:其中红框、绿框、蓝框框的代码就是三次递归调用的过程。
例子流程详细中间过程:
从func letterCombinations
中的 { dfs(index: 0) }
letters = abc
,然后进入第9行代码也就是for循环流程
str[0] = "a"
,然后执行第11行dfs方法
letters
,此时letters = "jkl"
,然后进入for循环流程,执行到第20行,str[1] = "j"
,然后执行第21行dfs方法
letters
,此时letters = "tuv"
,然后进入for循环流程,执行到第20行,str[2] = "t"
,然后执行第31行dfs方法
list.append(str)
,然后return
,【此时str = "ajt"
】。
letters = "tuv"
,for循环继续,此时的letter
改为u
。然后执行第31行的dfs
详细的过程我通过伪代码和举例子的方式来详细的说明了。大家可以根据上述的伪代码思路走一遍,应该可以很快的理解。
今天主要通过这个排列组合的题目,引出了dfs深度优先搜索算法,然后通过使用dfs来解决了这道leetcode的算法题。因为该算法包含递归,为了便于大家理解,我通过伪代码和大家详细的介绍了其实现流程也就是如何回退的,其实站到高一点想想,递归本身就是先疯狂压栈然后再出栈的,所以有一个天然回退的特性。那今天就分享到这啦,希望能帮助到大家。
end