简体   繁体   中英

Letter Combinations of a Phone Number (Java) - Passing an array through functions

Problem

Given a digit string, return all possible letter combinations that the number could represent. (Check out your cellphone to see the mappings) Input:Digit string "23", Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]

Question

I'm confused about the solution code below from LeetCode . Why does passing the result array through recursive calls change the result array in letterCombinations ? Is it because the result array in ever recursive getString call is referencing the same result array?

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);
    }
}

Is it because the result array in ever recursive getString call is referencing the same result array?

The answer is yes .

Why does passing the result array through recursive calls change the result array in letterCombinations?

The passing of an array result in letterCombinations changes the array and getString call is referencing the same result array. As it is a recursive method call, it gets upadtes after each iteration and stores the value to the same reference. That is the main reason, why you have different values for each iteration or a recursive call. Thus it affects the actual array as well.

Firstly I'll point out that despite the name of the site you got it from, this isn't especially clear code.

The call to getString() has three changing parameters - digits , temp and result .

map never changes - it would be better and clearer if it were a constant. Let's pretend it is, so the signature of getString() is getString(String digits, List<Character> temp .

The naming isn't obvious, but temp contains the "work done so far", so the first time we call it, it's an empty list.

Let's look at what happens the first time it is called, with digits == 234 and temp an empty list:

  • digits.length() != 0 -- so we skip the whole of the first block.
  • we grab the first digit, 2 and look up its letters in the map - "a"
  • we loop through the letters:
    • we put 'a' onto the end of temp , making temp == ['a']
    • then we call getString("34", ['a'])
    • we remove the last item from temp , making temp == []
    • then the same with 'b' -- getString("34",['b'])
    • then the same with 'c' -- getString("34",['c'])

Then we're done. But what happened in those recursive calls?

Follow the logic through getString("34",['a']) and you'll see how it grabs 3 from its local digits and makes calls like getString("4", ['a','d']) .

In turn getString("4", ['a','d']) makes calls like getString("",['a','d','g']) .

Finally we're at level where the recursion stops. Look at what happens when we call getString("",['a','d','g']) :

  • digits.length == 0 , so we go into the if block and return -- we don't progress into the part that would call getString() again.
  • we (in a bit of a laborious way) join the chars from temp into a String , and add it to result .
  • And that's it.

Better code:

 if(digits.isEmpty()) {
     result.add(String.join("",temp));
     return;
 }

We have never created a new result - we're just passing the same one (and the same map too) to every invocation of getString() . So when one getString() adds an item, that item's still there when the next getString() adds a second.


Recursive methods can usually be read as:

 def recursivemethod(params) {
      if(it's a no-brainer) {
          output an answer
      } else {
          do a little bit of the job
          call recursiveMethod(newParams)
      }
 }

In this case, it's a no-brainer when digits is empty -- the whole of the answer is in temp and just needs adding to the result list.

If it's not a no-brainer, the "little bit of the job" is to handle the first digit, recursing for each possible letter it could represent.


Cleaner in my opinion, while maintaining the spirit of the original:

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);
        }
    }
}

By building an immutable string prefix + letters.charAt(i) rather than manipulating a mutable List<Character> , you avoid having to put it back the way you found it, making code that's much easier to understand.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM