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.
Related
I have to write a piece of code for a class that counts the occurrences of characters within an input file and then sorts them by that, and I chose to do that by creating an ArrayList where each object[] has two elements, the character and the number of occurrences.
I was trying to increment the integer representing the number of occurrences and I just couldn't get that to work
My current attempt looks like this:
for(int i=0;i<=text.length();i++) {
if(freqlist.contains(text.charAt(i))) {
freqlist.indexOf(text.charAt(i))[1]=freqlist.get(freqlist.indexOf(text.charAt(i)))[1]+1;
}
}
text is just a string containing all of the input file
freqlist is declared earlier as
List<Object[]> freqlist=new ArrayList<Object[]>();
So, I was wondering how one could increment or modify an element of an array that is inside of an arraylist
In General there are 3 mistakes in your program which prevent it from working. It cannot work because the for loop has i<=text.length() and it should be i < text.length(), otherwise you will have exception. Second mistake is that you use freqlist.contains(...) where you assume both elements of object arrays are the same, or in other words the array is the equal, which is wrong assumption. Third mistake is using freqlist.indexOf(...) which relies on array equality again. I made the example working although this data structure List<Object[]> is inefficient for the task. It is best to use Map<Character,Integer>.
Here it is:
import java.util.ArrayList;
import java.util.List;
class Scratch {
public static void main(String[] args) {
String text = "abcdacd";
List<Object[]> freqlist= new ArrayList<>();
for(int i=0;i < text.length();i++) {
Object [] objects = find(freqlist, text.charAt(i));
if(objects != null) {
objects[1] = (Integer)objects[1] +1;
} else {
freqlist.add(new Object[]{text.charAt(i), 1});
}
}
for (Object[] objects : freqlist) {
System.out.println(String.format(" %s => %d", objects[0], objects[1]));
}
}
private static Object[] find(List<Object[]> freqlist, Character charAt) {
for (Object[] objects : freqlist) {
if (charAt.equals(objects[0])) {
return objects;
}
}
return null;
}
}
The way I would do this is first parse the file and convert it to an array of characters. This would then be sent to the charCounter() method which would count the number of times a letter occurs in the file.
/**
* Calculate the number of times a character is present in a character array
*
* #param myChars An array of characters from an input file, this should be parsed and formatted properly
* before sending to method
* #return A hashmap of all characters with their number of occurrences; if a
* letter is not in myChars it is not added to the HashMap
*/
public HashMap<Character, Integer> charCounter(char[] myChars) {
HashMap<Character, Integer> myCharCount = new HashMap<>();
if (myChars.length == 0) System.exit(1);
for (char c : myChars) {
if (myCharCount.containsKey(c)) {
//get the current number for the letter
int currentNum = myCharCount.get(c);
//Place the new number plus one to the HashMap
myCharCount.put(c, (currentNum + 1));
} else {
//Place the character in the HashMap with 1 occurrence
myCharCount.put(c, 1);
}
}
return myCharCount;
}
You could use some Stream magic, if you are using Java 8 for the grouping:
Map<String, Long> map = dummyString.chars() // Turn the String to an IntStream
.boxed() // Turn int to Integer to use Collectors.groupingBy
.collect(Collectors.groupingBy(
Character::toString, // Use the character as a key for the map
Collectors.counting())); // Count the occurrences
Now you could sort the result.
I am trying to find out if there is a possibility of returning the updated ArrayList after removing the specified element at the index in a single line so that I can pass it on to the recursive function.
Below is a snippet of my code which tries to generate all valid parenthesis combinations given n pairs of "()" brackets.
My concern is in the recursive function call "findAllCombinations" where after some validations I want to remove one character at each recursive call from the arrayList courceSet. However sourceSet.remove(index) returns a character. Instead I want to pass the updated list after removing the character in one line. Is it possible ?
Note : The line below is syntactically wrong and just used for better illustration.
findAllCombinations(sourceSet.remove(index), soFar + singleBracket, singleBracket); .
I did go through the official documentation but did not find any help.
Any help is appreciated, and thanks for your time.
public class GenerateParenthesis {
char singleBracket;
List<String> answerSet = new ArrayList<String>();
char[] repoSet = {'(',')'};
public List<String> generateParenthesis(int n) {
String soFar = "(";
List<Character> sourceSet = new ArrayList<Character>();
for(int i = 0;i<n;i++){
sourceSet.add('(');
sourceSet.add(')');
}
findAllCombinations(sourceSet,soFar,'(');
return answerSet;
}
public void findAllCombinations(List<Character> sourceSet,String soFar,Character toRemove){
if(sourceSet.isEmpty()){
answerSet.add(soFar); // append to a answer set list containing all combinations
return;
}
for(int i = 0;i<2;i++){
singleBracket = repoSet[i];
int index = sourceSet.indexOf(singleBracket);
if(index!=-1) {
findAllCombinations(sourceSet.remove(index), soFar + singleBracket, singleBracket);
}
}
}
public static void main(String args[]){
GenerateParenthesis gp = new GenerateParenthesis();
List<String> ans = new ArrayList<String>();
ans = gp.generateParenthesis(3);
}
}
ArrayList (likely to most List implementations) is a mutable data structure: calling remove you modify the list rather than returning a new list without the removed element.
If you want the latter behavior, the quick and easy way is to do a copy of the list.
// (inside the if...)
// pass the original list to the constructor to make a copy
List<Character> sourceSetCopy = new ArrayList<>(sourceSet);
// modify the copy
sourceSetCopy.remove(index);
// use the modified copy
findAllCombinations(sourceSetCopy, soFar + singleBracket, singleBracket);
Situation
Well, this method is managing a conversion which accepts a list as a parameter, but definately doesn't look scalable.
List<Long> array_list_data= new ArrayList<>();
public void get_Data() {
long0 = array_list_data.get(0);
long1= array_list_data.get(1);
long2= array_list_data.get(2);
}
Afterwards, it will create a different class with the long fields.
Problem
However, what if we have to expand this data to a 100 parameters on this list?
What I have done so far is:
List<Long> array_list_data= new ArrayList<>();
public void get_Data() {
int k = 0;
long0= array_list_data.get(k);
long1= array_list_data.get(k++);
long2= array_list_data.get(k++);
}
Why incrementing k is not the right way to do it?
k++ performs a post-increment. In other words, the value of the expression is the original value of k, and then k is incremented. It's still incremented before the method is called, but the value passed as the argument is the value before the increment takes place. In other words, a call of:
x = list.get(k++);
is equivalent to:
int tmp = k;
k = k + 1;
x = list.get(tmp);
So if you actually had:
memory_version = array_list_data.get(k++); // Calls with 0, then k=1
mains_voltage_min = array_list_data.get(k++); // Calls with 1, then k=2
mains_voltage_max = array_list_data.get(k++); // Calls with 2, then k=3
then it would be fine, and equivalent to your first code. Your current problem is that you've actually got:
memory_version = array_list_data.get(k); // Calls with 0, then k=0
mains_voltage_min = array_list_data.get(k++); // Calls with 0, then k=1
mains_voltage_max = array_list_data.get(k++); // Calls with 1, then k=2
However, I'd suggest that if you're modelling the data in a class using a collection as a field, you may well be better off with a separate field for each value. (You may create an instance of the class by extracting the data from a list, of course, but that's a different matter.)
k++ will return the value of k then increment it.
++k will increment k then return the incremented value.
You should use ++k in your situation.
It works fine, just k++ does first return k and then increment it by one, so you get k, k, k+1, k+2, etc. Use ++k instead. Or use k++ in the first call, too, your choice.
Although your approach works fine with some tweaking of ++ position, with 100 fields you may be better off with reflection. Put field names into an array or a list, then go through them one by one in a loop, and set values based on a loop variable:
String[] fieldNames = new String[] {"memory_version", " mains_voltage_min", ...};
...
Class<MyClass> c = MyClass.class;
for (int i = 0 ; i != fieldNames.length ; i++) {
Field f = c.getDeclaredField(fieldNames[i]);
f.setLong(this, array_list_data.get(i));
}
This reduces your list processing code to a few simple lines, and lets you change the order of fields in array_list_data simply by arranging your fieldNames array in proper order.
You don't need to maintain the index variable at all; this is why we have iterators:
final Iterator<Integer> iterator = array_list_data.iterator();
memory_version = iterator.next();
mains_voltage_min = iterator.next();
mains_voltage_max = iterator.next();
To manage scalability, I'd use an enum and a Map:
enum Var {
MEMORY_VERSION(0),
MAINS_VOLTAGE_MIN(1),
MAINS_VOLTAGE_MAX(2);
private Integer value;
Var(Integer value) {
this.value = value;
}
public Integer value() { return value; }
}
List<Long> array_list_data= new ArrayList<>();
Map<Integer, Long> variables = new HashMap<>();
public void getData() {
for (int j=0; j<array_list_data.size(); j++) {
variables.put(j, array_list_data.get(j));
}
}
public void test() {
System.out.println("Memory version: " + variables.get(Var.MEMORY_VERSION.value()));
}
so that you can add as many vars as you need, and you can retrieve with a meaningful name, like in the test() method.
Just about the incrementing part, try this:
public void getData() {
int i = 0;
// get element 0, then increment i
memory_version = array_list_data.get(i++);
// get element 1, then increment i
mains_voltage_min = array_list_data.get(i++);
// get element 2, then increment i
mains_voltage_max = array_list_data.get(i++);
}
That's how I do it for example when working with JDBC ResultSet.
Hi community I have a question, I happen to have an array of objects loaded on startup, through that generate array another array of integers that contains your code, it appears that array of integers'm removing their values, what I want is to compare the list of integer array currently have with the array of objects, and remove all code object that whole array mentioned is found.
My code java:
private List<ValidColumnKey> columnCustomer;
private int[] selectedCustomer;
public void init(){
this.setColumnCustomer(new ArrayList<ValidColumnKey>());
this.getColumnCustomer().add(new ValidColumnKey(1, "Codigo", "code"));
this.getColumnCustomer().add(new ValidColumnKey(2, "Nombre", "name"));
this.getColumnCustomer().add(new ValidColumnKey(3, "Nombre Comercial", "comercialName"));
this.getColumnCustomer().add(new ValidColumnKey(4, "Estado", "isActive"));
this.setSelectedCustomer(new int [this.getColumnCustomer().size()]);
int i = 0;
for(ValidColumnKey column : this.getColumnCustomer()){
this.getSelectedCustomer()[i] = column.getCodigo();
i++;
}
}
I mean I would have my array of integers with codes removed, like this:
selectedCustomer = [1, 2, 3];
What I wanted was to remove from the list of objects that do not have codes in the array of integers, but it is not my code:
List<ValidColumnKey> auxRemoColumnKeys = new ArrayList<ValidColumnKey>();
for(ValidColumnKey column : this.getColumnCustomer()){
for(Integer codigo : this.getSelectedCustomer()){
if (column.getCodigo() != codigo) {
auxRemoColumnKeys.add(column);
break;
}
}
}
this.getColumnCustomer().remove(auxRemoColumnKeys);
I could guide the solution.
this.getColumnCustomer().remove(auxRemoColumnKeys);
This statement assumes you have a valid equals method for your class ValidColumnKey, which I suspect is probably not the case.
What you want to do is iterate with a Iterator. Some sample code could be like
Set<Integer> toRemoveCodes = new HashSet<Integer>(Arrays.asList(1, 2, 3));
for (Iterator<ValidColumnKey> it = this.getColumnCustomer().iterator(); it.hasNext(); ) {
ValidColumnKey curColumnKey = it.next();
Integer code = curColumnKey.codigo();
if (toRemoveCodes.contains(code)) {
it.remove();
}
}
There are multiple reasons your current attempt is failing. The first is that this line:
if (column.getCodigo() != codigo) {
Is testing for object equivalence between Integers, not value equavalence between ints. If you want to compare Integers, you have to use the equals method:
if (!column.getCodigo().equals(codigo)) {
However, if getCodigo returns an int and getSelectedCustomer returns an int[] then this line should be changed instead:
for(int codigo : this.getSelectedCustomer()){
Because you didn't need to use Integer in the first place.
Secondly, this line attempts to remove auxRemoColumnKeys itself so you probably mean removeAll:
this.getColumnCustomer().remove(auxRemoColumnKeys);
Lastly, your logic is generally flawed. It basically says "for each element in getColumnCustomer, if getCodigo is not equal to all of getSelectedCustomer remove it". I don't think that's what you've intended.
This is a modified loop that uses the same "add to a list and remove the list items" procedure but the logic will work:
List<ValidColumnKey> auxRemoColumnKeys = new ArrayList<ValidColumnKey>();
int[] selected = this.getSelectedCustomer();
for (ValidColumnKey column : this.getColumnCustomer()) {
int i = 0;
for ( ; i < selected.length; i++) {
/* note: if getCodigo returns an Integer change this check to
* "if (column.getCodigo().equals(selected[i])) {"
*/
if (column.getCodigo() == selected[i]) {
break;
}
}
/* this says "if the search loop did not break early" */
if (i == selected.length) {
auxRemoColumnKeys.add(column);
}
}
this.getColumnCustomer().removeAll(auxRemoColumnKeys);
I am new to Java. I want to Parse the data which is in this Format
Apple;Mango;Orange:1234;Orange:1244;...;
There could be more than one "Orange" at any point of time. Numbers (1,2...) increase and accordingly as the "Orange".
Okay. After splitting it, Lets assume I have stored the first two data(Apple, Orange) in a variable(in setter) to return the same in the getter function. And now I want to add the value(1234,1244....etc) in the 'orange' thing into a variable to return it later. Before that i have to check how many oranges have come. For that, i know i have to use for loop. But don't know how to store the "Value" into a variable.
Please Help me guys.
String input = "Apple;Mango;Orange:1234;Orange:1244;...;"
String values[] = input.split(";");
String value1 = values[0];
String value2 = values[1];
Hashmap< String, ArrayList<String> > map = new HashMap<String, ArrayList<String>>();
for(int i = 2; i < values.length; i = i + 2){
String key = values[i];
String id = values[i+1];
if (map.get(key) == null){
map.put(key, new ArrayList<String>());
}
map.get(key).add(id);
}
//for any key s:
// get the values of s
map.get(s); // returns a list of all values added
// get the count of s
map.get(s).size(); // return the total number of values.
Let me try to rephrase the question by how I interpreted it and -- more importantly -- how it focuses on the input and output (expectations), not the actual implementation:
I need to parse the string
"Apple;Mango;Orange:1234;Orange:1244;...;"
in a way so I can retrieve the values associated (numbers after ':') with the fruits:
I should receive an empty list for both the Apple and Mango in the example, because they have no value;
I should receive a list of 1234, 1244 for Orange.
Of course your intuition of HashMap is right on the spot, but someone may always present a better solution if you don't get too involved with the specifics.
There are a few white spots left:
Should the fruits without values have a default value given?
Should the fruits without values be in the map at all?
How input errors should be handled?
How duplicate values should be handled?
Given this context, we can start writing code:
import java.util.*;
public class FruitMarker {
public static void main(String[] args) {
String input = "Apple;Mango;Orange:1234;Orange:1244";
// replace with parameter processing from 'args'
// avoid direct implementations in variable definitions
// also observe the naming referring to the function of the variable
Map<String, Collection<Integer>> fruitIds = new HashMap<String, Collection<Integer>>();
// iterate through items by splitting
for (String item : input.split(";")) {
String[] fruitAndId = item.split(":"); // this will return the same item in an array, if separator is not found
String fruitName = fruitAndId[0];
boolean hasValue = fruitAndId.length > 1;
Collection<Integer> values = fruitIds.get(fruitName);
// if we are accessing the key for the first time, we have to set its value
if (values == null) {
values = new ArrayList<Integer>(); // here I can use concrete implementation
fruitIds.put(fruitName, values); // be sure to put it back in the map
}
if (hasValue) {
int fruitValue = Integer.parseInt(fruitAndId[1]);
values.add(fruitValue);
}
}
// display the entries in table iteratively
for (Map.Entry<String, Collection<Integer>> entry : fruitIds.entrySet()) {
System.out.println(entry.getKey() + " => " + entry.getValue());
}
}
}
If you execute this code, you will get the following output:
Mango => []
Apple => []
Orange => [1234, 1244]