电话号码的字母组合(Java) - 通过函数传递数组

时间:2017-09-14 15:03:04

标签: java arrays recursion

问题

给定一个数字字符串,返回该数字可能代表的所有可能的字母组合。 (查看你的手机以查看映射)输入:数字字符串" 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);
    }
}

2 个答案:

答案 0 :(得分:0)

是因为递归getString调用中的结果数组是引用相同的结果数组吗?

答案是

为什么通过递归调用传递result数组会改变letterCombinations中的result数组?

result中传递数组letterCombinations会更改数组,而getString调用会引用相同的结果数组。由于它是一个递归方法调用,它在每次迭代后获得upadtes并将值存储到同一个引用。这是主要原因,为什么每次迭代或递归调用都有不同的值。因此它也会影响实际的数组。

答案 1 :(得分:0)

首先,我要指出,尽管您从中获得了该网站的名称,但这并不是特别清晰的代码。

getString()的调用有三个不断变化的参数 - digitstempresult

map永远不会改变 - 如果它是一个常数会更好更清晰。我们假装它是,所以getString()的签名是getString(String digits, List<Character> temp

命名并不明显,但temp包含到目前为止完成的工作&#34;所以我们第一次调用它时,它是一个空列表。< / p>

让我们看看第一次调用时会发生什么,digits == 234temp为空列表:

  • 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>