2

The line of code return tournamentTreeKSelection(listToArray(list), k); is causing an infinite recursion in my program and I am unable to discover the exact cause.

import java.util.ArrayList;
import java.util.Arrays;

public class TournamentTree {

public static int tournamentTreeKSelection(int[] data, int k) {
    ArrayList<Integer> list = new ArrayList<>();
    ArrayList<Integer> list2 = new ArrayList<>(list);

        for(int i = 0; i < data.length - 1; i += 2) {
            list.add(max(data[i] , data[i + 1]));
        }

        if(list.size() % 2 != 0) list.add(-1);

        if(k > 1 && list.size() == 1) {
            for(int i = 0; i < list2.size(); i++)
                if(list2.get(i) == list.get(0))
                    list2.remove(i);

            return tournamentTreeKSelection(listToArray(list2),--k);
        }

        if(list.size() == 1) return list.get(0);

        return tournamentTreeKSelection(listToArray(list), k);

}

public static int max(int a, int b) {
    return a > b ? a : b;
}

public static int[] listToArray(ArrayList<Integer> arr) {
    int[] arr2 = new int[arr.size()];
    for(int i = 0; i < arr.size(); i++)
        arr2[i] = arr.get(i);

    return arr2;
}


}

I have done a hand trace using the array [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] and I am able to get down to a single element [10], why then is the run of the program causing a stack overflow ?

Mutating Algorithm
  • 2,604
  • 2
  • 29
  • 66
  • Be careful of the term *infinite recursion*. Normally this is not possible as excessive recursion will inevitably cause the call stack to overflow. – Luke Joshua Park Dec 03 '15 at 21:06
  • what is the value of `k`? – njzk2 Dec 03 '15 at 21:07
  • @Stackflowed why? list is modified in the function - in particular the length is modified. – EkcenierK Dec 03 '15 at 21:09
  • this `if(list.size() % 2 != 0) {list.add(-1);}` means `list.size() == 1)` will never be true. – njzk2 Dec 03 '15 at 21:09
  • @njzk2 It crashes for any value of K – Mutating Algorithm Dec 03 '15 at 21:09
  • @njzk2 can I move that line anywhere else ? I still need it as part of the algorithm – Mutating Algorithm Dec 03 '15 at 21:10
  • 1
    Note that `list2` is always empty, since you initialize it with `ArrayList list2 = new ArrayList<>(list);` when `list` is still empty. This means your iteration over list2 does nothing. I'm assuming this is not the intended behavior. – Eran Dec 03 '15 at 21:12
  • 1
    @MutatingAlgorithm I don't know. I don't understand what your algorithm is supposed to do. for example, `for(int i = 0; i < list2.size(); i++)` does nothing because `list2` is empty. – njzk2 Dec 03 '15 at 21:13
  • What is the code for the original call to this function? What is the intended use of the function? – Eric G Dec 03 '15 at 21:14
  • The purpose of the algorithm is to find the kth largest element in an array – Mutating Algorithm Dec 03 '15 at 21:15
  • @MutatingAlgorithm ok. Then you are overly complicating things. For starters, the loop with `i+=2` messes things up. – njzk2 Dec 03 '15 at 21:19
  • @njzk2 I specifically have to use a tournament tree to find the kth largest such that an input array of [10, 9, 8, 7 ... 1] produces [10,8,6,4,2,-1] in the first pass through of the function ultimately producing [10]. This is easy if k = 1 but I don't know how to modify it for k > 1 – Mutating Algorithm Dec 03 '15 at 21:21
  • @MutatingAlgorithm I don't understand the point of the `[10,8,6,4,2,-1]` array. If the initial array is sorted, there is no point in the algorithm. Else, in this array you have no idea what order `8` is supposed to have, because you may have eliminated `9` against `10` (in which case the order is 3), or you may have eliminated `3` (in which case the order is 2). – njzk2 Dec 03 '15 at 21:26
  • @MutatingAlgorithm i dont understand the point of finding the kth largest item using a tournament tree. – Eric G Dec 03 '15 at 21:30
  • Have you made sure, that your code don't just return tournamentTreeKSelection(listToArray(list), k); with same k and some array that's never modified? That's probably why you get your error. – Jonas Tonny Dec 03 '15 at 21:30
  • @MutatingAlgorithm It just doesn't make sense, the kth largest element can be eliminated on the first recursive call, what is the point of continuing the recursion? – Eric G Dec 03 '15 at 21:32
  • Is the kth largest the kth bracket, or the kth largest out of the list. So if k = 2 and list is [4,3,5,1,7,8] what is the answer? – Eric G Dec 03 '15 at 21:33
  • @EricG so apparently a tournament tree really is a binary heap. so your list would not exist. – njzk2 Dec 03 '15 at 21:36
  • @EricG I don't know how to implement it using a binary heap, that's why i've been using arrays the whole time. :( – Mutating Algorithm Dec 03 '15 at 21:38
  • @njzk2 that makes a lot more sense than what I was imagining a tournament tree to be [pic](http://www.printyourbrackets.com/thumbs/4-Team-Single-Elimination.gif) – Eric G Dec 03 '15 at 21:38
  • @MutatingAlgorithm Google it, [Binary Heap](https://en.wikipedia.org/wiki/Binary_heap) – Eric G Dec 03 '15 at 21:39
  • @MutatingAlgorithm the array is the binary heap (or it should be) – njzk2 Dec 03 '15 at 21:39
  • @MutatingAlgorithm depending on the order of the games in your tournament, `7` could be eliminated by `8` early in the tournament, and the second-best (finalist) could be `5` too. – njzk2 Dec 03 '15 at 21:41
  • @njzk2 You have the right idea of what I was going for with your picture of a tournament tree. – Mutating Algorithm Dec 03 '15 at 21:42
  • @MutatingAlgorithm the problem I see here is that a tournament is only able to sort partially, and to give the winner. The other competitors are not sorted with each other across branches. I don't see how to apply a k-selection based on that. For example, there are 4 loosers in quarter final. how do they compare? – njzk2 Dec 03 '15 at 22:12
  • 1
    Possible duplicate of [What is a debugger and how can it help me diagnose problems](http://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) – Raedwald Dec 18 '15 at 19:39

4 Answers4

4

Here is how I would do that, considering more or less the same algorithm:

  • find the max value
  • if k is 1, return that value
  • recurse with the list of values filtered of that max value, and k - 1.

Like that

public static int tournamentTreeKSelection(int[] data, int k) {
    int max = -1;
    for (int i : data) {
        max = Math.max(max, i);
    }
    if (k == 1) {
        return max;
    }
    List<Integer> results = new ArrayList<>();
    for (int i : data) {
        if (i != max) {
            results.add(i);
        }
    }
    return tournamentTreeKSelection(listToArray(results), k - 1);
}

The part where you select half of the item makes no sense to me, as you lose information of the relative position of the other elements. (So I just removed it).

Siyual
  • 16,415
  • 8
  • 44
  • 58
njzk2
  • 38,969
  • 7
  • 69
  • 107
1

A better answer to giving you a solution is for you to understand the concept of a binary heap (tournament tree).

There are plenty of questions asked on this site alone, and here is a good explanation of your problem.

Note - this is slighlty different, find up to kth largest elements

Answer

Community
  • 1
  • 1
Eric G
  • 928
  • 1
  • 9
  • 29
1

njzk2's answer is indeed simpler than what you are trying to do, since it eliminates the tournament tree concept.

If you are required to use the tournament implementation, I'll give you some pointers regarding why your code doesn't work.

Once you find the max number using the tournament method, you should remove it from the original list and then call the method again for an array of the remaining n-1 numbers and k-1, since your task is now to find the k-1'th highest number of the remaining numbers.

You try to do it with list2, but you are not initializing list2 correctly. You initialize it to be empty. You must keep track of the original array, so that you can remove from it the max element each time you find the max element of the remaining array. For this purpose, you should add another array parameter to your method. Initially it will contain the original array, and each time you find the maximum number, it will contain the array that you get by removing the maximum number.

Perhaps you can simplify the implementation by using two recursive methods, one method for finding the maximum number using a tourmant tree and a second method that would use the first one to find the k'th highest number.

This makes sense, since you have two different recursive steps that don't work well together - one reduces the array in half until you reach a single element while the other removes a single element from the array and reduces k, until k reaches 1.

public static int getKthElement (int[] data, int k)
{
    int max = findMaxByTournament (data);
    if (k == 1) {
        return max;
    } else {
        // create an array containing the lowest data.length-1 
        // elements of the data array
        int[] data2 = new int[data.length-1];
        boolean foundMax = false;
        int count = 0;
        for (int n : data) {
            if (n < max || foundMax) {
                data2[count++] = n;
            }
            if (n == max) {
                foundMax = true; // this flag is required in case more than
                                 // one element has the maximum value
            }
        }
        return getKthElement (data2, k - 1);
    }
}

public static int findMaxByTournament (int[] data)
{
    // here you do something similar to what you already did - find the
    // max number of each pair and call this method recursively with half
    // the elements until you have one remaining element 
}
Eran
  • 387,369
  • 54
  • 702
  • 768
0

I added comment to code (I find a lot of problem with recursion):

public static int tournamentTreeKSelection(int[] data, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        ArrayList<Integer> list2 = new ArrayList<>(list);


        for(int i = 0; i < data.length - 1; i += 2) {
            list.add(max(data[i] , data[i + 1]));
        }

        if(list.size() % 2 != 0) list.add(-1);



    if(k > 1 && list.size() == 1) { // if list.size() % 2 != 0, we never go to this, 
// because we never decrease list.count and never decrease k before this block  
            for(int i = 0; i < list2.size(); i++)
                if(list2.get(i) == list.get(0))
                    list2.remove(i);

            return tournamentTreeKSelection(listToArray(list2),--k);
        }

        // if we add two element or more in list, we never stop after that
        if(list.size() == 1) return list.get(0);

        // we never decrease k, so call tournamentTreeKSelection with same parameters -> it's run forever   
        return tournamentTreeKSelection(listToArray(list), k);    
}

// main problem that listToArray returns copy of list, but you run tournamentTreeKSelection(listToArray(list), k) with same parameters, if you listToArray returns int[] less that list, tournamentTreeKSelection can stop 
public static int[] listToArray(ArrayList<Integer> arr) {
...
Slava Vedenin
  • 58,326
  • 13
  • 40
  • 59