I've been trying to understand the following code that uses Depth-First-Search (DFS) to print out all unique combinations of length k comprising of numbers [1..n]
Please see the line commented "doubt" in the private dfs function
public ArrayList<ArrayList<Integer>> combine(int n, int k) {
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
if (n <= 0 || n < k)
return result;
ArrayList<Integer> item = new ArrayList<Integer>();
dfs(n, k, 1, item, result);
return result;
}
private void dfs(int n, int k, int start, ArrayList<Integer> item,
ArrayList<ArrayList<Integer>> res) {
if (item.size() == k) {
res.add(new ArrayList<Integer>(item)); /*doubt*/
return;
}
for (int i = start; i <= n; i++) {
item.add(i);
dfs(n, k, i + 1, item, res);
item.remove(item.size() - 1);
}
}
If I change it to res.add(item) it returns result as list of null lists. ListObject.add(E e) is a perfectly valid function, why doesn't it work here?
So your question concerns these two alternatives:
// works
res.add(new ArrayList<Integer>(item));
// won't work, results in empty lists
res.add(item);
The purpose of new ArrayList<Integer>(item) is to create a new list with the same content as the original, effectively cloning the original.
If you don't clone the original, it will stay empty. The first time dfs is called, item is empty, and then look at this loop:
for (int i = start; i <= n; i++) {
item.add(i);
dfs(n, k, i + 1, item, res);
item.remove(item.size() - 1);
}
Every element added to item will be later removed. This is why you end up with empty lists without the cloning step. Without cloning, not only you get a list of empty lists, all of the empty lists are actually the same original ArrayList that you created in combine, before the first call to dfs.
It's because item.remove(item.size() - 1); is modifying the same list that you just added to your list of results. So it always ends up removing all the items. The solution you have is actually copying the list of items and storing them in your result list. No one has a reference to that list so it doesn't get modified.
Related
I ran into an issue while getting value for a List<List<Integer>> called res. It's a recursive call and that call is supposed to fill this "res", however when the recursive function is called by another function and that function is returning an empty list. But when I do log it, I can see the values. Here's the code:
class Solution {
public List<List<Integer>> combinationSum(int[] arr, int k) {
// given an arr and a sum k, find all the subsequences that sum upto k
// we can use backtracking here as we need all the options
// there are 2 choices for every element:
// either we can take it in the subres or discard it
// if we take it once, we can take it n times till the target is reached
Arrays.sort(arr);
List<List<Integer>> res = new ArrayList<>();
List<Integer> subRes = new ArrayList<>();
combinationSum(0, arr, k, 0, res, subRes);
return res;
}
public void combinationSum(int i, int[] arr, int k, int sum,
List<List<Integer>> res,
List<Integer> subRes){
if(sum == k) {
if(!res.contains(subRes)) res.add(subRes);
System.out.println("res = " + res);
return;
}
if(i == arr.length || sum > k) return;
subRes.add(arr[i]);
sum += arr[i];
combinationSum(i, arr, k, sum, res, subRes);
subRes.remove(new Integer(arr[i]));
sum -= arr[i];
combinationSum(i + 1, arr, k, sum, res, subRes);
}
}
I'm stuck badly on this,
Thanks in advance.
The mutations you make to subRes in the recursion is affecting what you see in res. Before adding subRes into res, make a new copy of it.
if(!res.contains(subRes)) {
res.add(new ArrayList<>(subRes));
}
In your code, correct list (subRes) is added to res, but you mutate/modify subRes by removing elements sometime later when the recursive call returns.
I don't know how to add element if the type data in squareBracket is array.
If you want to add element in List then simply do:
listObject.add(new Object[]{});
Or if you want to add to Object array inside list then use (Which is quite a long root due to restrictive behavior of array data structure, Thats why ArrayList is alternative to achieve this. ):
#Test
public void testArray() {
List<Object[]> listObjects = new ArrayList<>();
listObjects.add(new Object[]{1,2});
addX( listObjects.get(0).length , listObjects.get(0) , 3);
listObjects.set(0,addX( listObjects.get(0).length , listObjects.get(0) , 3));
System.out.println(listObjects.get(0));
}
public Object[] addX(int n, Object arr[], Object x)
{
int i;
// create a new array of size n+1
Object newarr[] = new Object[n + 1];
// insert the elements from
// the old array into the new array
// insert all elements till n
// then insert x at n+1
for (i = 0; i < n; i++)
newarr[i] = arr[i];
newarr[n] = x;
return newarr;
}
I'm trying to implement the merge sort algorithm on a list of strings of size N, and I've managed to get it to sort, but for some reason the original values are being added onto the end of the sorted list.
I'm quite new to implementing sorting algorithms (read: very new), so would really appreciate anyone letting me know if I've missed something.
public static void mergeSortWords(int n, List<String> words) {
if (n < 2) {
return;
}
int mid = n / 2; // Getting the mid-point of the array
List<String> l = new ArrayList<String>(mid); // Left side of array
List<String> r = new ArrayList<String>(n-mid); // Right side of array
for (int i = 0; i < mid; i++) {
l.add(i, words.get(i));
}
for (int j = mid; j < n; j++) {
r.add(j - mid, words.get(j));
}
mergeSortWords(mid, l); // recursively sort the left side
mergeSortWords(n-mid, r); // recursively sort the right side
mergeWords(n, words, l, r, mid, n-mid); // merge the sorted arrays back together
}
public static void mergeWords(int n, List<String> words, List<String> l, List<String> r, int left, int right) {
if (words.size() > n) {
return;
}
int i = 0, j = 0, k = 0;
while (i < left && j < right) {
if (l.get(i).compareToIgnoreCase(r.get(j)) < 0) { // comparing the strings alphabetically
words.add(k++, l.get(i++));
}
else {
words.add(k++, r.get(j++));
}
}
while (i < left) {
words.add(k++, l.get(i++));
}
while (j < right) {
words.add(k++, r.get(j++));
}
}
I unit tested like so:
#Test
public void mergeSortWordsTest() {
List<String> actual = new ArrayList<String>();
List<String> expected = new ArrayList<String>();
actual.add("hello");
actual.add("yo");
actual.add("hi");
actual.add("what");
actual.add("bottle");
expected.add("bottle");
expected.add("hello");
expected.add("hi");
expected.add("what");
expected.add("yo");
mergeSortWords(actual.size(), actual);
Assert.assertEquals(expected, actual);
And I receive:
java.lang.AssertionError:
Expected :[bottle, hello, hi, what, yo]
Actual :[bottle, hello, hi, what, yo, hello, yo, hi, what, bottle]
Thank you for any pointers!
Because the words list you pass to mergeWords is never cleared. mergeWords will just add new elements to this list without caring about the elements that it already contains. Simply do a
words.clear();
at the beginning of mergeWords.
Alternatively, you can overwrite the existing elements with .set(int index, E element) instead of .add(). But you need to make sure that the list is of the correct size.
A few unrelated comments:
In your function calls, you are always passing the size of the lists as an additional parameter (n, left, right). This is redundant (you can get the size with list.size()). Anything that is redundant can easily become inconsistent (i.e., what happens if you pass a wrong size?). So it is better to remove those parameters.
When you add an element to a list, you use the overload add(int index, E element). This is perfectly fine, but I think using the overload add(E element) is much easier to handle as you don't need to keep track of where to add the elements. The overload will just append the new element to the end of the list.
I already have a list type Integer with values in it and I want to test sequentially from index zero if the sum of one range of elements satisfy a particular value then copy this range in an list and store it in a list of linkedlist. Then again test sequentially but now from the following index of the previous range, so if the previous range was index 0 to index 9 then start at index 10, and repeat the process until the last index.
List<Integer> arrayB = new LinkedList<Integer>(); //this is the array with values in it
List<LinkedList> p = new LinkedList<LinkedList>();// this is the array of arrays
List<Integer> arrayA = new LinkedList<Integer>();// this is the range or the sub list of arrayB
public void function(int n)// suppose that n = 6 and arrayB have these value {1,2,3,1,1,1,1,2}
{
int count = 0;
for (int w : arrayB)
{
count = w + count;
arrayA.add(w);
if(count == n)
{
count = 0;
p.add((LinkedList) arrayA);
arrayA.clear();
}
}
}
However, this code fail when I call method clear in arrayA so is there any alternative to code with this logic regardless of the data structure used?
My understanding of the problem is the following:
There exists an array from which you would like to extract a certain range of values given that they satisfy some criteria. In this case, the criterion is that the range evaluates to some sum. Once this has been completed, you would like to repeat the process until all of the values in the original data-structure have been exhausted.
I will assume that your original data-structure is an array of integers, and that your resulting data-structure is a linkedlist of integer arrays.
One way to do it may be to keep a global counter that keeps track of the current index of the original array, such as something like the following:
int[] originalArray = {//list of numbers separated by commas};
LinkedList<Integer[]> resultingList = new LinkedList<>();
int currentIndex = 0;
public static void function(int totalSum) {
int currentSum = 0;
int initialIndex = currentIndex;
while((currentSum != totalSum) && (currentIndex < (originalArray.length - 1))) {
if(currentSum + initialArray[currentIndex] <= totalSum) {
currentSum += initialArray[currentIndex];
currentIndex++;
}
else {
break;
}
}
if(currentSum = totalSum) {
int[] arrayToAdd = new int[currentIndex - initialIndex - 1];
for(int i = 0; i < currentIndex - initialIndex; i++) {
arrayToAdd[i] = originalArray[initialIndex + i];
}
resultingList.add(arrayToAdd);
}
}
You are using the same list reference arrayA every time you add a sub list into p, every list element in p is pointing to the same arrayA . So when you call arrayA.clear(); You clear all the list elements in p.
To correct that, you need to create a new list object when you add a sublist to arrayA:
public static void function(int n)// suppose that n = 6 and arrayB have these value {1,2,3,1,1,1,1,2}
{
int count = 0;
LinkedList<Integer> subList = new LinkedList<>();
for (int w : arrayB) {
count = w + count;
subList.add(w);
if (count == n) {
count = 0;
p.add((LinkedList) subList); // p is adding a new list reference every time
subList = new LinkedList<>(); // create a new list object, subList points to a new list object
}
}
}
The issue is that when you add your linked list into the final storage p, you are assuming the elements of the list are put in there. Only a pointer is referenced, so when you clear it the next line, all the elements are gone.
p.add((LinkedList) arrayA);
arrayA.clear();
One tip is to move arrayA's scope to inside the function. This is because it's temporary, and only a sublist, so it shouldn't be at the instance level. It can be reused by doing a
arrayA = new LinkedList<Integer>();
and when doing so, you haven't lost the old list because p is keeping a reference to it.
Another tip is to name your lists with meaningful names.
originalIntList, groupedIntList, singleGroupIntList help the reader figure out what they could be doing more than a comment stating obvious aspects of the Java object.
One list with names:(unsorted) e.g [paul, foul, mark]
Another list with integers: e.g [5, 2, 6]
The values on the second list are the numbers "selected" by each person(name), so
paul has number 5, foul's number is 2, mark's number is 6.
I'm trying to sort the names' list based on the values of the second list on a descending order. I cant use a map as i need both lists on other occasions on my program.
With the sorting method i did i get a list like that: [paul, mark, foul]
As you can see, its not sorted as i would want.
The correct one would be: [mark,paul,foul]
But i cant find the fault on the code.
public ArrayList<String> sortNames(ArrayList<Integer> results){
String tmp;
for (int k=0; k<Names.size()-1; k++) {
boolean isSorted=true;
for (int i=1; i<Names.size()-k; i++) {
if (results.get(i)>results.get(i-1) ) {
tmp=Names.get(i);
Names.set(i,Names.get(i-1));
Names.set(i-1,tmp);
isSorted=false;
}
}
if (isSorted) break;
}
return Names;
}
EDIT!!! with the help of the answers below,the code is:
public ArrayList<String> sortNames(ArrayList<Integer> results){
String tmp2;
int tmp;
for (int k=0; k<Names.size()-1; k++) {
boolean isSorted=true;
for (int i=1; i<Names.size()-k; i++) {
if (results.get(i)>results.get(i-1) ) {
tmp=results.get(i);
results.set(i,results.get(i-1));
results.set(i-1,tmp);
tmp2=Names.get(i);
Names.set(i,Names.get(i-1));
Names.set(i-1,tmp2);
isSorted=false;
}
}
if (isSorted) break;
}
return Names;
}
This code works properly(for small lists)
I've just the query why it doesnt work for objects like ImageIcon. Any ideas?
Get rid of the two Lists. If the data is related then the data should be stored together in a simple class. Then the entire class is added to a list where you can sort on the individual properties if required. You can use a Bean Comparator to sort this List however you desire.
You're sort of sorting the List Names based on values of the List results... and it only terminates because of the condition k<Names.size()-1. Such a condition is usually not needed at all in bubblesort, which shows that there's something wrong.
You'd have to swap elements in both lists, not only in Names. This is the answer, but be warned that bubblesort is one of the worst algorithms ever.
Edit:
I cant use a map as i need both lists on other occasions on my program.
For sure you can (assuming the numbers are unique):
Map<Integer, String> m = new HashMap<Integer, String>();
for (int i=0; i<results.size(); ++i) m.put(results.get(i), Names.get(i));
Collections.sort(results);
for (int i=0; i<results.size(); ++i) Names.set(i, m.get(results.get(i));
There may be errors, but the idea should be clear.
There's another solution using a class of pairs (result, name), which works even with non-unique numbers, if you need it.
A slightly shorter solution:
Map<Integer, String> m = new TreeMap<Integer, String>();
for (int i=0; i<results.size(); ++i) m.put(results.get(i), Names.get(i));
Names.clear();
Names.addAll(m.values());
This is based on properties of TreeSet.values "The collection's iterator returns the values in ascending order of the corresponding keys" and List.addAll "Appends all of the elements in the specified collection to the end of this list, in the order that they are returned by the specified collection's iterator"
Build a list of pairs of (name, value) by taking elements from the two lists pairwise (having a class that stores the two values as fields). Implement Comparable to compare the value field.
Sort the result with Collections.sort().
Extract the names from the sorted list.
There you go w/ the vanilest quicksort you'd ever get, the simplest principle is: when you swap the 1st list, swap the 2nd as you go. Hopefully that's not a homework, but even if it's not...
In short, copy/paste and enjoy the quickSort(list1, list2) is all you need. The lists are sorted by the 1st list natural oredering define by Comparable.
private static void swap(List<?> l1, List<?> l2, int i, int j){
Collections.swap(l1, i, j);
Collections.swap(l2, i, j);
}
private static <T extends Comparable<? super T>> int partition(List<T> comp, List<?> l2, int left, int right){
int i = left, j = right;
T pivot = comp.get((left + right) / 2);
while (i <= j) {
while (comp.get(i).compareTo(pivot)<0)
i++;
while (comp.get(i).compareTo(pivot)>0)
j--;
if (i <= j) {
swap(comp, l2, i++, j--);
}
};
return i;
}
private <T extends Comparable<? super T>> void quickSort(List<T> comp, List<?> l2, int left, int right) {
int index = partition(comp, l2, left, right);
if (left < index - 1)
quickSort(comp, l2, left, index - 1);
if (index < right)
quickSort(comp, l2, index, right);
}
public <T extends Comparable<? super T>> void quickSort(List<T> comp, List<?> l2) {
if (comp.size()<l2.size())
throw new IndexOutOfBoundsException();
quickSort(comp, l2, 0, comp.size());
}
Create a temporary mapping name->number, then sort names with custom comparator which uses the mapping:
Map<String, Integer> m = new HashMap<String, Integer>;
for(int i = 0; i < Names.size(); i++)
m.put(Names.get(i), results.get(i));
Collections.sort(Names, new Comparator<String>() {
#Override
public int compare(String s1, s2) { return m.get(s2) - m.get(s1); }
});
This solution will work even if some numbers are equal.