3

I've searched through many questions on this site with somewhat similar underlying concepts, however after many hours of attempting to solve this problem myself and reviewing I am still lost. If there is another question that answers this I will be more than happy to give it a look over.

Ultimately I want to create a recursive method such that it takes two lists and returns a Set of String lists:

//Example of such a function definition
private static Set<List<String>> myRecursiveMethod(List<String> listOne, 
List<String> listTwo) {
}

When I say "Set of String lists" I mean specifically the following: (Note:"AD" == "DA")

// if the two following lists are INPUTTED into myRecursiveMethod();
// listOne = ["A","B"]
// listTwo = ["C","D"]
// the following set is OUTPUTTED: [["AC","BD"],["AD","BC"]] 

Such that if there were three elements in both listOne and listTwo, there would be SIX elements in the set. i.e:

// listOne = ["A","B","C"]
// listTwo = ["D","E","F"]
// OUTPUTTED: [["AD","BE","CF"],["AD","BF","CE"],["BD","AE","CF"],
// ["BD","AF","CE"],["CD","AE","BF"],["CD","AF","BE"]] 

I tried writing this using a double enhanced FOR loop so I could understand the logic. My FOR loop approach is terrible and only works for the HARD-CODED limit of list.size() == 2.

// Create Lists and append elements 
List<String> listOne = new ArrayList<String>();
listOne.add("A");
listOne.add("B");

List<String> listTwo = new ArrayList<String>();
listTwo.add("C");
listTwo.add("D");

// List One = ["A","B"]
// List Two = ["C","D"]

// Create new List
List<List<String>> newList = new ArrayList<List<String>>();
Integer counter = 0;

    for (String s : listOne) {
        counter++;
        for (String p : listTwo) {

            // A HARD-CODED bad implementation of this method
            if (counter < 3) {
                List<String> newListTwo = new ArrayList<String>();
                newListTwo.add(s.concat(p));
                newList.add(newListTwo);

            } else if (!(counter % 2 == 0)) {
                newList.get(1).add(s.concat(p));

            } else {
                newList.get(0).add(s.concat(p));
            }
        }
    }
    System.out.println(newList); // = [["AC","BD"],["AD","BC"]] 

Also you can note that I defined List<List<String>> Rather than Set<List<String>>. This was due to my badly coded attempted which relies on the list.get() method.

So my current recursive method is as follows:

private static Set<List<String>> myRecursiveMethod(List<String> listOne, 
List<String> listTwo) 
{
    //Base Case:
    if (listOne.isEmpty){
        return new HashSet<List<String>>;
    }
    //Recursive Case:
    else {
        String listOneFirst = listOne.get(0);
        String listTwoFirst = listTwo.get(0);

        List<String> sampleList = new ArrayList<String>();
        sampleList.add(listOneFirst+listTwoFirst);

        Set<List<String>> newSet = new HashSet<List<String>>(myRecursiveMethod())
        newSet.add(sampleList);
        return newSet;
    }
}

This method only acts like this currently:

INPUT:

  • List One = ["A","B"]
  • List Two = ["C","D"]

OUTPUT:

  • [["AC"]["BD"]]

DESIRED OUTPUT:

  • [["AC","BD"],["AD","BC"]]

EDIT:

After reviewing responses my W.I.P code for the class:

private static Set<List<String>> myRecursiveMethod(List<String> listOne,
        List<String> listTwo) {

    //Backup Case (user enters an empty list)
    if (listOne.isEmpty()){
        return new HashSet<List<String>>();
    }

    // Base Case:
    if (listOne.size() == 1) {
        List<String> mergedStrings = new ArrayList<>();
        for (String s : listTwo) {
            mergedStrings.add(listOne.get(0).concat(s));
        }
        Set<List<String>> builtHashSet = new HashSet<List<String>();
        builtHashSet.add(mergedStrings);
        return builtHashSet;
    }
    // Recursive Case:
    else {

        // Ensure original list values arn't changed.
        List<String> newListOne = new ArrayList<String>(listOne);
        List<String> newListTwo = new ArrayList<String>(listTwo);

        //first two elements...I don't think this is correct
        String listOneFirst = newListOne.get(0);
        String listTwoFirst = newListTwo.get(0);
        List<String> sampleList = new ArrayList<String>();
        sampleList.add(listOneFirst + listTwoFirst);

        //used for making recursive case smaller
        newListOne.remove(0);

        // Calls recursion
        Set<List<String>> newSet = new HashSet<List<String>>(
                myRecursiveMethod(newListOne, newListTwo));
        newSet.add(sampleList);

        return newSet;
    }
}
7
  • Just to understand that detail: this is homework, and you are asked to create a recursive method? As I think a non-recursive method should be straight forward ... Commented May 8, 2017 at 6:29
  • It's not directly coursework. I am using this to further my knowledge in recursion and I've based the method off of a similar question I was asked previously but was never able to solve. Commented May 8, 2017 at 6:33
  • I do not really see the need for recursion here, as there is always a fixed number of input parameters (2). If I understand the rules for producing the strings correctly, there are two steps: find all permutations of the second list's items, then concat them with the elements from the first list. Iteration is perfectly fine here; and if you split it up into two steps you should be able to untangle it and make easily make it independant of the lists' length. Commented May 8, 2017 at 6:35
  • Maybe this can help you in this case: stackoverflow.com/a/10305419/7653073. It is a recursive approach to generating the permutations of a list. Commented May 8, 2017 at 6:42
  • In a real life application I would most definitely approach this with Iteration as it is what makes the most sense to me. However I would like to learn from what I could not previously solve and hence. @MalteHartwig Thanks for the link I will give it a look over now. Commented May 8, 2017 at 6:49

1 Answer 1

2

I think the problem is here:

if (listOne.isEmpty){
    return new HashSet<List<String>>;
}

You are correct, at some point your recursion has to end, and you have to start building the desired output. But the desired output is not a Set with an empty list. It is a Set containing some lists with some content. Thus: don't wait until listOne is empty. Instead:

if (listOne.size() == 1) {
  List<String> mergedStrings = new ArrayList<>();
  mergedStrings = ... merge the ONE listOne entry with all listTwo entries
  Set<List<String>> rv = new HashSet<>();
  rv.add(mergedStrings);
  return rv;
}

In other words: you use recursion to reduce the length of the first list by one. And when only one element is left in that list, it is time to merge in the second list.

Now lets look into how to "use" that (calling the method rec for brevity); putting down some pseudo code to show the steps we need:

rec([a, b], [c,d]) -->
rec([a], [c,d]) X rec([b], [c, d]) -->
<[ac, ad]> X <[bc, bd]> -->
<[ac, ad], [bc, bd]>

"X" meaning "joining" two results from recursive calls; should be as easy as:

Set<List<String>> rec1 = rec(...);
return rec1.addAll(rec2 ...
Sign up to request clarification or add additional context in comments.

4 Comments

I'll give this a try now, and see how I go.
Following this through my mergedStrings becomes a single list of all the possible listOne entry + listTwo entries: for (String s : listTwo) { mergedStrings.add(listOne.get(0).concat(s)); } However if I want any entry (i.e. A, B, C or D) to only occur once with a single list object I'm stuck on what I am to be recursing. I've added an update of my new Code (not sure if this is standard procedure for questions).
Thanks @GhostCat, big help, i'll go through it when I have the time :)
Oh right, I'm still new to this whole service, will do.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.