how to understand this recursion in the leecode? - java

new to java,I read the answer in the leecode ,and it ask for a array like [1,2,3]and return its permutation [1,3,2],[2,1,3].....and feel confused especially this code
Collections.swap(output, first, i);
backtrack(n, output, res, first + 1);
I do not know why the use Collections.swap(output,first,i) I think in first loop ,the first and i is equal to 0,so why use swap here. they are same vaule. what this recursion actually do,I debug it and can not figure out.code is below:
package com.company;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Main {
public static void main(String[] args) {
int[]arr=new int[]{1,2,3};
Main main1=new Main();
List<List<Integer>> lists = main1.permute(arr);
System.out.println(lists);
}
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
List<Integer> output = new ArrayList<Integer>();
for (int num : nums) {
output.add(num);
}
int n = nums.length;
backtrack(n, output, res, 0);
return res;
}
public void backtrack(int n, List<Integer> output, List<List<Integer>> res, int first) {
if (first == n) {
res.add(new ArrayList<Integer>(output));
}
for (int i = first; i < n; i++) {
Collections.swap(output, first, i);
backtrack(n, output, res, first + 1);
Collections.swap(output, first, i);
}
}
}

According to the documentation, the swap() method of java.util.Collections class is used to swap the elements at the specified positions in the specified list. If the specified positions are equal, invoking this method leaves the list unchanged.
So in a recursive technique, it is ok to have that call even though it does nothing in that particular condition, just to make the logic easier to implement and understand. If you want to avoid that, you will un-necessarily need to bring conditional statements into the logic, which is really not required.

Here is a somewhat simplified and commented version that may help follow what the code does:
import java.util.*;
public class Main {
public static void main(String[] args) {
int[]arr=new int[]{1,2,3};
List<List<Integer>> permutations = new Main().permute(arr);
System.out.println(permutations);
}
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
List<Integer> input = new ArrayList<>();
for (int num : nums) {
input.add(num);
}
backtrack(input, result, 0);
return result;
}
public void backtrack(List<Integer> input, List<List<Integer>> result, int currentIndex) {
if (currentIndex == input.size() ) { //index passed the end of the collection
result.add(new ArrayList<>(input)); //add the permutation to the returned result
//the method return here because when currentIndex == input.size()
//next for loop will not be executed
//you may add a return here. It may improve the readability of the code
}
//iterate over each element of the array form currentIndex to the end
for (int i = currentIndex; i < input.size(); i++) {
Collections.swap(input, currentIndex, i);//create a permutation by swapping
backtrack(input, result, currentIndex + 1); //increment currentIndex and process new permutation
Collections.swap(input, currentIndex, i);//undo permutation before next loop
}
}
}

Related

Making a main method to test linked lists

I need help to make a main method to test this program I've made for an assignment
Write a method to merge two linked lists of integers that are sorted into descending order. The result should be a third linked list that is the sorted combination of the original lists. Do not destroy the original lists.
import java.util.Iterator;
import java.util.LinkedList;
public class Exercise6
{
public static LinkedList<Integer> merge(LinkedList<Integer> a,LinkedList<Integer> b)
{
//Initialize variables
LinkedList<Integer> result = new LinkedList<Integer>();
Iterator<Integer> aI = a.iterator();
Iterator<Integer> bI = b.iterator();
int aTemp = 0;
int bTemp = 0;
//Get the first values from both lists using the next method
aTemp = aI.next();
bTemp = bI.next();
while(aI.hasNext() && bI.hasNext())
{
//Comparing the two elements
if(aTemp > bTemp)
{
result.add(bTemp);
bTemp = bI.next();
}
}
if(!aI.hasNext())
{
result.add(aTemp);
}
while(aI.hasNext())
{
result.add(aTemp);
aTemp = aI.next();
}
while(bI.hasNext())
{
result.add(bTemp);
bTemp = bI.next();
}
if(!aI.hasNext())
{
result.add(aTemp);
}
else
{
result.add(bTemp);
}
return result;
}
}
I think this is all you are asking for:
LinkedList<Integer> list1 = new LinkedList<Integer>();
list1.add(9);
list1.add(7);
list1.add(3);
LinkedList<Integer> list2 = new LinkedList<Integer>();
list1.add(8);
list1.add(5);
list1.add(1);
Exercise6 test = new Exercise6();
test.merge(list1,list2)
First, you are missing an else for your if in while(aI.hasNext() && bI.hasNext()). Next, I would strongly recommend you program to the List interface (instead of the concrete LinkedList type). Also, I would make the merge method generic on some comparable type T. Something like,
public static <T extends Comparable<? super T>> List<T> merge(List<T> a, List<T> b) {
// Initialize variables
List<T> result = new LinkedList<>();
Iterator<T> aI = a.iterator();
Iterator<T> bI = b.iterator();
// Get the first values from both lists using the next method
T aTemp = aI.hasNext() ? aI.next() : null;
T bTemp = bI.hasNext() ? bI.next() : null;
while (aI.hasNext() && bI.hasNext()) {
// Comparing the two elements
if (aTemp.compareTo(bTemp) < 0) {
result.add(bTemp); // <-- add the right-hand side
bTemp = bI.next();
} else {
result.add(aTemp); // <-- add the left-hand side
aTemp = aI.next();
}
}
// Add the final two values from the loop.
if (aTemp.compareTo(bTemp) < 0) {
result.add(bTemp);
result.add(aTemp);
} else {
result.add(aTemp);
result.add(bTemp);
}
while (aI.hasNext()) { // Add any remaining values from a
result.add(aI.next());
}
while (bI.hasNext()) { // Add any remaining values from b
result.add(bI.next());
}
return result;
}
Then you can test merge like
public static void main(String[] args) {
System.out.println(merge(Arrays.asList(6, 4, 2), Arrays.asList(5, 3, 1)));
System.out.println(merge(Arrays.asList("bat", "ant"),
Arrays.asList("dog", "cat")));
}
I get
[6, 5, 4, 3, 2, 1]
[dog, cat, bat, ant]
So do you write your code without ever testing it? I would suggest you get familiar with writing driver classes since as your code gets bigger you will need to test it along the way.
Create another class in the same package of your Exercise class: you can call it ExerciseDriver. Import classes as needed.
Declare Initialize and populate two linked lists. Declare a third linked lost to store the result.
Call the static method of your Exercise6 class
Print the result to verify
You could do something like:
import java.util.LinkedList;
public Class ExcerciseDriver{
public static void main (String[] args){
LinkedList<Integer> list1 = new LinkedList<>();
LinkedList<Integer> list2 = new LinkedList<>();
LinkedList<Integer> resultList;
list1.add(77);
list1.add(7);
list1.add(6);
list2.add(100);
list2.add(43);
list2.add(8);
resultList = Excercise6.merge(list1, list2);
System.out.println(resultList);
}
}
Now all you have to do is running the main method and verify the correctness of your algorithm

Use Recursion to get Subsets of an array. C++ and Java give me different results

For example, given a set like below -
S=[1,3]
we want to get the list of list with following values:
[[],[1],[3],[1,3]]
I used C++ with the following code and it worked perfect for me.
However, after I changed it to Java, the code didn't give me right results.
Any help? This is my Java code:
public static List<List<Integer>> subsetsRecursive(int[] nums){
List<List<Integer>> res = new ArrayList<List<Integer>>();
if(nums.length == 0){
return res;
}
ArrayList<Integer> itemList = new ArrayList<Integer>();
dfs(res, itemList, 0, nums);
return res;
}
private static void dfs(List<List<Integer>> res, ArrayList<Integer> temp, int end, int[] nums) {
if(end == nums.length) {
res.add(temp);
return;
}
temp.add(nums[end]);
dfs(res, temp, end+1, nums);
temp.remove(temp.size()-1);
dfs(res, temp, end+1, nums);
}
And this is C++:
class Solution {
private:
vector<vector<int> >res;
public:
vector<vector<int> > subsets(vector<int> &S) {
res.clear();
vector<int>tmpres;
dfs(S, 0, tmpres);
return res;
}
void dfs(vector<int> &S, int iend, vector<int> &tmpres)
{
if(iend == S.size())
{res.push_back(tmpres); return;}
tmpres.push_back(S[iend]);
dfs(S, iend+1, tmpres);
tmpres.pop_back();
dfs(S, iend+1, tmpres);
}
};
In the line
res.add(temp); temp is a reference.
You are adding a reference to the same list (itemList) every time you add it.
Try changing it to something list res.add(new ArrayList(temp)); so that it copies the list instead.

Java Calculate all possible combinations of given int array

I am trying to construct a program that would take an array of int({1,2,3} and a length value and calculate all possible combinations of this array.
For example:
int[] arr= new char[] {0,1};
int[] tes = new int[3];
possiblecomb(2, arr,tes,0);
This will output:
00
10
01
11
But i keep getting a Stack overflow error when i try to call the possiblecomb in the for loop
import java.util.Arrays;
public class Program {
public static void main(String[] args) {
// Create an arr to work with
int[] test = new int[] {0,1};
int[] tes = new int[3];
// Find all possible combinations of this arr in the string size of 3
possiblecomb(3, test,tes,0);
}
public static void possiblecomb(int maxLength, int[] nums, int[] curr,int end) {
// If the current array has reached it's maximum length
if(end == maxLength) {
System.out.println(Arrays.toString(curr));
// Else add each number from the numbs to new array and process these new arrays again
} else {
for(int i = 0; i < nums.length; i++) {
int[] oldCurr = curr.clone();
curr[end]= nums[i];
possiblecomb(maxLength,nums,curr,end++);
curr = oldCurr.clone();
}
}
}
}
Try moving your recursive call outside of the for.
You are using the for in order to copy contents.
Your end variable will eventually increment above max lenght, and your (==) comparison won't be a stopper.
Take the example where num.Length = 2 and end is 2 :
You will call your function once with end = 3 which will stop and print inside the recursive call, and next, when i == 1 your end will be 4 and the recursive call won't break.
If you want to avoid the infinite recurssion with your current code in order to better debug with output, put the break condition
if (end>=maxLength)
As #MichaelCMS said you never stop the recursion, hence a stack overflow.
If you don't mind using Lists instead of arrays this is a solution:
import java.util.*;
public class Program {
private static List<List<Integer>> combinations(List<Integer> list, int maxLength) {
return combinations(list, maxLength, new ArrayList(), new ArrayList());
}
private static List<List<Integer>> combinations(List<Integer> list, int length, List<Integer> current, List<List<Integer>> result) {
if (length == 0) {
List<List<Integer>> newResult = new ArrayList<>(result);
newResult.add(current);
return newResult;
}
List<List<List<Integer>>> res3 = new ArrayList<>();
for (Integer i : list) {
List<Integer> newCurrent = new ArrayList<>(current);
newCurrent.add(i);
res3.add(combinations(list, length - 1, newCurrent, result));
}
List<List<Integer>> res2 = new ArrayList<>();
for (List<List<Integer>> lst : res3) {
res2.addAll(lst);
}
return res2;
}
public static void printCombinations(List<Integer> list, int maxLength) {
List<List<Integer>> combs = combinations(list, maxLength);
for (List<Integer> lst : combs) {
String line = "";
for (Integer i : lst) {
line += i;
}
System.out.println(line);
}
}
public static void main(String[] args) {
List<Integer> l = Arrays.asList(0, 1);
printCombinations(l, 2);
}
}
That gives you:
00
01
10
11

java programming Exception in thread "main" what is this error?

I wrote this program to find the item and then to remove all elements which are smaller than the item. The are no compilation errors, but when I run the program, the following message appears.
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 5, Size: 5
at java.util.ArrayList.rangeCheck(ArrayList.java:653)
at java.util.ArrayList.remove(ArrayList.java:492)
at bb.deleteValues(bb.java:15)
at bb.main(bb.java:33)
Process completed. "
Code:
import java.util.ArrayList;
import java.util.Scanner;
public class bb {
public static boolean deleteValues(ArrayList<Integer> list, int item)
{ int p =list.indexOf(item);
if(!list.contains(item))
return false;
else
for(int i=p; i<list.size();i++ )
{int n=list.get(i);
if (n>list.get(i+1))
list.remove(p);
list.remove(i+1);
}
return true;
}
public static void main(String[] args) {
ArrayList<Integer> a= new ArrayList<Integer>(8);
a.add(3);
a.add(10);
a.add(4);
a.add(5);
a.add(3);
a.add(2);
a.add(8);
a.add(6);
a.add(4);
if(deleteValues(a,4))
for(int x : a)
System.out.print(x+ " ");
}
}
Your deleteValues method loops i from p to list.size()-1, but the loop's body contains list.get(i+1) and list.remove(i+1), so when i==list.size()-1, list.get(i+1) and list.remove(i+1) will attempt to access an item from an index not present in the list, which is the cause of the exception.
Removing the elements smaller than the passed item requires iterating over all the elements in the list, and comparing each one to the passed item. Note that when you remove the i'th element from the list, the (i+1)'th element becomes the new i'th element. That's why i should be incremented only if you don't remove an element from the list.
public static boolean deleteValues(ArrayList<Integer> list, int item)
{
int p = list.indexOf(item);
if(p<0)
return false;
for(int i=0; i<list.size();) {
if (list.get(i) < item) {
list.remove(i);
} else {
i++;
}
}
return true;
}
list.remove(i+1); will exceed the list size. For ex, when you are passing the 4th element, it will search for the element at 5th postion
The problem is in your deleteValues function, you loop while i is less than the size of the ArrayList, but add 1 when you call the get function, which can and will cause an IndexOutOfBoundsException, because it can make i equal to the size of the ArrayList, which is invalid. Change your for loop, and remove the cases where you have i+1, like so:
for(int i=p; i<list.size();i++ )
{
int n=list.get(i);
if (n>list.get(i))
list.remove(p);
list.remove(i);
}
Also, if your function is meant to remove all copies of a certain value, the code can be much, much simpler, and clearer:
public static boolean deleteValues(ArrayList<Integer> list, int item)
{
if (list.contains(item))
{
for (int i = 0; i < list.size(); i++)
if (list.get(i) == item)
list.remove(i);
return true;
}
return false;
}
import java.util.ArrayList;
import java.util.Scanner;
public class Ex {
public static boolean deleteValues(ArrayList<Integer> list)
{
for(int i=0; i<list.size();i++ )
{int n=list.get(i);
if (n>list.get(i+1))
// list.remove(i);
list.remove(i+1);
}
return true;
}
public static void main(String[] args) {
ArrayList<Integer> a= new ArrayList<Integer>(8);
a.add(3);
a.add(10);
a.add(4);
a.add(5);
a.add(3);
a.add(2);
a.add(8);
a.add(6);
// a.add(4);
if(deleteValues(a))
for(int x : a)
System.out.print(x+ " ");
}
}
you are adding one extra object to the arraylist with itz size specified and you are using index values wrongly in the deleteValues method so plz check with the both codes.
import java.util.ArrayList;
import java.util.Scanner;
public class Ex {
public static boolean deleteValues(ArrayList<Integer> list, int item)
{ int p =list.indexOf(item);
if(!list.contains(item))
return false;
else
for(int i=0; i<list.size();i++ )
{int n=list.get(i);
if (n>list.get(i+1))
list.remove(p);
//list.remove(i+1);
}
return true;
}
public static void main(String[] args) {
ArrayList<Integer> a= new ArrayList<Integer>(8);
a.add(3);
a.add(10);
a.add(4);
a.add(5);
a.add(3);
a.add(2);
a.add(8);
//a.add(6);
a.add(4);
if(deleteValues(a,4))
for(int x : a)
System.out.print(x+ " ");
}
}

Logical Error in finding subsets

I'm trying to generalize the code to find all subsets of a given string(elements that are repeated will be treated as distinct) into one that would work for any list.
public class Subsets{
private static <T> void RecursiveSubsets(List<List<T>> list, ArrayList<T> soFar, List<T> rest)
{
if(rest.isEmpty())
{
list.add(soFar);
}
else
{
List<T> remaining;
if(rest.size() == 1)
{
remaining = new ArrayList<T>();
}
else
{
remaining = rest.subList(1, rest.size() - 1);
}
//include the element
ArrayList<T> includeFirst = new ArrayList<T>(soFar);
includeFirst.add(rest.get(0));
RecursiveSubsets(list, includeFirst, remaining);
//exclude the element
RecursiveSubsets(list, soFar, remaining);
}
}
public static <T> List<List<T>> getAllSubsets(List<T> set)
{
List<List<T>> subsets = new ArrayList<List<T>>();
RecursiveSubsets(subsets,new ArrayList<T>(),set);
return subsets;
}
public static void main(String [] args)
{
List<Integer> ints = new ArrayList<Integer>(){
{
add(0);add(1);add(2);add(3);
}
};
List<List<Integer>> allSubsets = getAllSubsets(ints);
System.out.println("Total Subsets returned : " + allSubsets.size());
for(int i=0; i<allSubsets.size(); ++i)
{
for(int j=0; j<allSubsets.get(i).size(); ++j)
{
System.out.print(allSubsets.get(i).get(j) + " ");
}
System.out.println();
}
}
}
After a few attempts I was able to get this to compile but this is what I get as output.
Even if I have more integers, it still returns this. I'm not able to figure out what I have missed and need help finding it.
$ java Subsets
Total Subsets returned : 4
0 1
0
1
Your program is actually almost correct, and you just have the sublist logic a bit wrong.
The javadoc for List.sublist says
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive.
The word "exclusive" here is critical.
If you just change
remaining = rest.subList(1, rest.size() - 1);
to
remaining = rest.subList(1, rest.size());
your code works.
The logic of this (in pseudocode) is typically:
List<List<T>> subsets( List<T> list ){
if( list is empty ) return a list containing the empty list;
// else:
subsetsWithout = subsets( list w/o 0th element );
result.addAll(subsetsWithout);
for( subset in subsetsWithout )
result.add( subset + list[0] )
return result;
}
It looks like what you're doing is different, and the fact that you're trying to return things through the function parameters is making it more confusing.

Categories

Resources