Java程序中的无限递归

时间:2015-12-03 21:04:25

标签: java algorithm recursion

代码行return tournamentTreeKSelection(listToArray(list), k);导致程序无限递归,我无法找到确切的原因。

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;
}


}

我使用数组[10,9,8,7,6,5,4,3,2,1]进行了手动跟踪,我能够归结为单个元素[10],为什么然后呢?是程序的运行导致堆栈溢出?

4 个答案:

答案 0 :(得分:4)

考虑到或多或少相同的算法,我将如何做到这一点:

  • 找到最大值
  • 如果k为1,则返回该值
  • 使用过滤了该最大值的值列表进行递归,并使用k - 1。

喜欢那个

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);
}

您选择项目一半的部分对我没有意义,因为您丢失了其他元素相对位置的信息。 (所以我把它删除了。)

答案 1 :(得分:1)

为您提供解决方案的更好答案是让您理解二进制堆(锦标赛树)的概念。

本网站上有很多问题,这里有一个很好的解释你的问题。

注意 - 这是非常不同的,找到第k个最大的元素

Answer

答案 2 :(得分:1)

njzk2的答案确实比你想要做的更简单,因为它消除了锦标赛树的概念。

如果您需要使用锦标赛实施,我会给您一些关于您的代码不起作用的指示。

使用锦标赛方法找到最大数量后,您应该将其从原始列表中删除,然后再次调用该方法以获取剩余n-1个数字和k-1的数组,因为您的任务现在要查找k-1个剩余数字的最高数量。

您尝试使用list2执行此操作,但未正确初始化list2。您将其初始化为空。您必须跟踪原始数组,以便每次找到剩余数组的max元素时可以从中删除max元素。为此,您应该为方法添加另一个数组参数。最初它将包含原始数组,每次找到最大数字时,它将包含通过删除最大数字得到的数组。

也许您可以通过使用两个递归方法来简化实现,一种方法用于使用旅游树查找最大数量,另一种方法将使用第一种方法来查找第k个最高数字。

这是有道理的,因为你有两个不同的递归步骤不能很好地协同工作 - 一个将数组减半,直到你到达一个元素,而另一个从数组中删除一个元素并减少k,直到k达到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 
}

答案 3 :(得分:0)

我在代码中添加了注释(我发现了很多递归问题):

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) {
...