问题
给定一个数字字符串,返回该数字可能代表的所有可能的字母组合。 (查看你的手机以查看映射)输入:数字字符串" 23",输出:[" ad"," ae"," af&# 34;," bd"," be"," bf"," cd"," ce",&# 34; CF"]
问题
我对LeetCode下面的解决方案代码感到困惑。为什么通过递归调用传递result
数组会更改result
中的letterCombinations
数组?是因为在递归getString
调用中的结果数组是引用相同的结果数组吗?
public List<String> letterCombinations(String digits) {
HashMap<Integer, String> map = new HashMap<>();
map.put(2, "abc");
map.put(3, "def");
map.put(4, "ghi");
map.put(5, "jkl");
map.put(6, "mno");
map.put(7, "pqrs");
map.put(8, "tuv");
map.put(9, "wxyz");
map.put(0, "");
ArrayList<String> result = new ArrayList<>();
if (digits == null || digits.length() == 0) {
return result;
}
ArrayList<Character> temp = new ArrayList<>();
getString(digits, temp, result, map);
return result;
}
public void getString(String digits, ArrayList<Character> temp, ArrayList<String> result,
HashMap<Integer, String> map) {
if (digits.length() == 0) {
char[] arr = new char[temp.size()];
for (int i = 0; i < temp.size(); i++) {
arr[i] = temp.get(i);
}
result.add(String.valueOf(arr));
return;
}
Integer curr = Integer.valueOf(digits.substring(0, 1));
String letters = map.get(curr);
for (int i = 0; i < letters.length(); i++) {
temp.add(letters.charAt(i));
getString(digits.substring(1), temp, result, map);
temp.remove(temp.size() - 1);
}
}
答案 0 :(得分:0)
是因为递归getString
调用中的结果数组是引用相同的结果数组吗?
答案是是。
为什么通过递归调用传递result
数组会改变letterCombinations中的result
数组?
在result
中传递数组letterCombinations
会更改数组,而getString
调用会引用相同的结果数组。由于它是一个递归方法调用,它在每次迭代后获得upadtes并将值存储到同一个引用。这是主要原因,为什么每次迭代或递归调用都有不同的值。因此它也会影响实际的数组。
答案 1 :(得分:0)
首先,我要指出,尽管您从中获得了该网站的名称,但这并不是特别清晰的代码。
对getString()
的调用有三个不断变化的参数 - digits
,temp
和result
。
map
永远不会改变 - 如果它是一个常数会更好更清晰。我们假装它是,所以getString()
的签名是getString(String digits, List<Character> temp
。
命名并不明显,但temp
包含到目前为止完成的工作&#34;所以我们第一次调用它时,它是一个空列表。< / p>
让我们看看第一次调用时会发生什么,digits == 234
和temp
为空列表:
digits.length() != 0
- 所以我们跳过整个第一个区块。2
并在地图中查找其字母 - "a"
'a'
放在temp
的末尾,temp == ['a']
getString("34", ['a'])
temp
删除了最后一项,并temp == []
'b'
- getString("34",['b'])
'c'
- getString("34",['c'])
然后我们就完成了。但那些递归调用中发生了什么?
按照逻辑getString("34",['a'])
进行操作,您就会看到它如何从3
抓取digits
并拨打getString("4", ['a','d'])
等电话。
反过来,getString("4", ['a','d'])
会调用getString("",['a','d','g'])
。
最后,我们处于递归停止的级别。看看我们致电getString("",['a','d','g'])
时会发生什么:
digits.length == 0
,因此我们进入if
区块并返回 - 我们不会再进入可再次拨打getString()
的部分。temp
中的字符加入String
,并将其添加到result
。更好的代码:
if(digits.isEmpty()) {
result.add(String.join("",temp));
return;
}
我们从未创建过新的result
- 我们只是将同一个(同样map
)传递给getString()
的每个调用。因此,当一个getString()
添加一个项目时,当下一个getString()
添加一个项目时,该项目仍然存在。
递归方法通常可以理解为:
def recursivemethod(params) {
if(it's a no-brainer) {
output an answer
} else {
do a little bit of the job
call recursiveMethod(newParams)
}
}
在这种情况下,当digits
为空时,它是明智的 - 整个答案都在temp
,只需要添加到结果列表中。
如果它不是一个简单的工作,那么这个工作的一点点就是&#34;是处理第一个数字,递归它可能代表的每个可能的字母。
在我看来更清洁,同时保持原始的精神:
private static final Map<Character, String> DECODINGS = new HashMap<>();
static {
DECODINGS.put('2', "abc");
// <snip>
}
public List<String> letterCombinations(String digits) {
ArrayList<String> result = new ArrayList<>();
addCombinationsToList(digits, "", result);
return result;
}
private void addCombinationsToList(String digits, String prefix, List<String> list) {
if (digits.isEmpty()) {
list.add(prefix);
} else {
String letters = DECODINGS.get(digits.charAt(0));
for (int i = 0; i < letters.length(); i++) {
addCombinationsToList(digits.substring(1), prefix + letters.charAt(i), list);
}
}
}
通过构建一个不可变的字符串prefix + letters.charAt(i)
而不是操纵一个可变的List<Character>
,你可以避免将它放回原来的方式,使代码更容易理解。< / p>