给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23" 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = "" 输出:[]
示例 3:
输入:digits = "2" 输出:["a","b","c"]
提示:
0 <= digits.length <= 4
digits[i]
是范围 ['2', '9']
的一个数字。这个问题可以通过回溯算法(Backtracking)来解决。回溯算法是一种通过探索所有可能的候选解来找出所有解的算法,它在探索过程中会尝试各种可能性,当发现当前路径不可能产生有效解时,会回退到上一步尝试其他可能性。在这个问题中,我们需要生成所有可能的字母组合,这些组合由输入的数字字符串 digits
决定。
具体步骤如下:
初始化:创建一个空列表 result
用于存储所有可能的字母组合。如果输入的 digits
字符串为空或者 null
,直接返回空列表。
递归函数:定义一个递归函数 backtrack
,它接收四个参数:当前的字母组合 current
,剩余的数字字符串 digits
,当前索引 index
,以及存储结果的列表 result
。
终止条件:在 backtrack
函数中,首先检查 index
是否等于 digits
的长度。如果是,说明已经处理完所有数字,当前的 current
就是一个完整的字母组合,将其添加到 result
列表中。
探索:如果 index
不等于 digits
的长度,获取当前数字对应的字母列表。遍历这个字母列表,对于每个字母,递归调用 backtrack
函数,将字母添加到 current
的末尾,并将 index
加一,继续探索。
回溯:在递归调用结束后,回退到上一步,尝试其他可能的字母组合。
返回结果:当所有可能的字母组合都被探索完毕后,返回 result
列表,它包含了所有可能的字母组合。
这个算法的时间复杂度是指数级的,因为每个数字可以对应多个字母,而且随着数字的增加,可能的组合数会迅速增长。但是,由于这个问题的输入规模通常较小,这个算法在实践中是可行的。
class Solution {
private String[] alpha = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
public List letterCombinations(String digits) {
List result = new ArrayList<>();
if (digits == null || digits.isEmpty()) {
return result; // 如果输入为空,直接返回空列表
}
backtrack(result, digits, 0, "");
return result;
}
private void backtrack(List result, String digits, int index, String current) {
// 当 index 等于 digits 的长度时,我们找到了一个完整的字母组合
if (index == digits.length()) {
result.add(current);
return;
}
// 获取当前数字对应的字母
String letters = alpha[digits.charAt(index) - '0'];
// 遍历当前数字对应的所有字母
for (char letter : letters.toCharArray()) {
backtrack(result, digits, index + 1, current + letter);
}
}
}
代码的时间复杂度和空间复杂度取决于输入字符串 digits
的长度以及每个数字对应的字母数量。
n
的字符串,每个数字 i
可以对应 1
到 4
个字母(根据 alpha
数组的定义)。因此,对于每个数字,我们最多有 4
种选择。n
,那么总的选择数将是 4^n
(在最坏的情况下,每个数字都对应 4
个字母)。alpha
数组中有些数字对应更少的字母(例如,0
对应空字符串,1
对应空字符串,2
对应 3
个字母),实际的复杂度会略低于 4^n
。具体来说,我们需要计算每个数字对应字母的实际数量,然后计算总的组合数。n
。result
,它在最坏的情况下将包含 4^n
个字符串,每个字符串的长度最多为 n
。O(4^n * n)
。在实际应用中,由于 alpha
数组中有些数字对应的字母数量少于 4
,实际的空间复杂度会低于这个上界。但是,由于这个问题的输入规模通常较小,这个算法在实践中是可行的。
1. 递归(Recursion):
backtrack
方法通过递归调用自身来生成所有可能的字母组合。2. 字符串处理(String Manipulation):
charAt
方法来获取字符串中的特定字符。length
方法来获取字符串的长度。isEmpty
方法来检查字符串是否为空。3. 字符数组(Character Array):
toCharArray
方法将字符串转换为字符数组,以便遍历和操作。4. 列表(List):
ArrayList
来存储和操作字符串列表。ArrayList
是 Java 中的一个动态数组,可以自动调整大小。5. 条件判断(Conditional Statements):
if
语句来检查输入字符串是否为空或 null
,并根据条件返回结果。6. 方法定义(Method Definition):
backtrack
来实现递归逻辑,该方法不直接暴露给外部调用,只在类的内部使用。7. 字符串拼接(String Concatenation):
+
运算符来拼接字符串,构建新的字母组合。8. 数据结构(Data Structures):
List
)作为数据结构来存储和返回结果。9. 边界条件处理(Boundary Conditions):
index
等于 digits
的长度时,表示已经处理完所有数字,此时将当前的字母组合添加到结果列表中。10. 回溯算法(Backtracking):
这个问题可以通过使用队列(Queue)实现的广度优先搜索(Breadth-First Search, BFS)算法来解决。
广度优先搜索是一种遍历图或树的算法,它从根节点开始,先访问所有相邻的节点,然后再逐层向下访问。
在这个特定问题中,我们将字符串的每个字母组合视为节点,数字到字母的映射关系视为边。
具体步骤如下:
1. 初始化:创建一个空的 ArrayList
来存储最终的字母组合结果,以及一个 LinkedList
作为队列,用于存储待处理的字母组合。
2. 队列操作:将空字符串(表示初始状态)添加到队列中。
3. 遍历数字:对于输入字符串 digits
中的每个数字,执行以下操作:
4. 结果收集:在遍历完所有数字后,队列中的所有元素即为所有可能的字母组合。将队列中的所有元素添加到结果列表中。
5. 返回结果:返回包含所有字母组合的列表。
class Solution {
private static final String[] KEYBOARD = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
public List letterCombinations(String digits) {
List result = new ArrayList<>();
if (digits == null || digits.length() == 0) {
return result;
}
Queue queue = new LinkedList<>();
queue.offer("");
for (char digit : digits.toCharArray()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
String current = queue.poll();
String letters = KEYBOARD[digit - '0'];
for (char letter : letters.toCharArray()) {
queue.offer(current + letter);
}
}
}
result.addAll(queue);
return result;
}
}
digits
的每个数字,对于每个数字,它会遍历队列中的所有当前组合,并对每个组合添加对应数字的所有字母。n
的字符串,每个数字最多对应 4
个字母(根据 KEYBOARD
数组的定义),所以对于每个数字,最多有 4
次操作。n
是字符串的长度。这是因为在最坏的情况下,每个数字都对应 4
个字母,并且每个字母组合都会产生新的组合。n
的字符串,最多会有 4^n
个字母组合。但是,由于队列在任何时候都只包含当前层级的组合,所以空间复杂度实际上是 O(4^n)。result
,但由于我们只存储最终的组合,所以这部分空间复杂度可以忽略不计。在实际应用中,由于 KEYBOARD
数组中有些数字对应的字母数量少于 4
,实际的空间复杂度会略低于 O(4^n)
。然而,由于这个问题的输入规模通常较小,这个算法在实践中是可行的。
1. 广度优先搜索(BFS):
2. 队列(Queue):
LinkedList
作为队列,用于存储待处理的字母组合。队列遵循先进先出(FIFO)的原则,适合用于 BFS。3. 字符串处理:
charAt
方法来获取字符串中的字符。length
方法来获取字符串的长度。toCharArray
方法将字符串转换为字符数组,以便遍历和操作。4. 递归与迭代:
5. 条件判断:
if
语句来检查输入字符串是否为空或 null
,并根据条件返回空列表。6. 数据结构:
ArrayList
来存储最终的字母组合结果,这是一个动态数组,可以自动调整大小。7. 集合操作:
offer
方法向队列中添加元素。poll
方法从队列中移除并返回队首元素。8. 循环控制:
for
循环来遍历队列中的元素,并使用 size
方法来获取队列的大小。9. 字符与字符串拼接:
+
运算符将字符拼接到字符串的末尾,形成新的字母组合。以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。