使用锦标赛树查找阵列中的第K个最大元素

时间:2015-12-03 13:40:26

标签: java arrays algorithm data-structures tree

下面,我设计了一个函数tournamentTreeKSelection,它使用数组模拟树状结构,并返回数组中最大的元素。例如,给定输入数组[10,9,8,7,6,5,4,3,2,1],执行以下步骤以返回10.

[10, 8, 6, 4, 2, -1]
[10, 6, 2, -1]
[10, 2]
[10]  //Max element of array found

我的目标是现在添加第二个参数int k,请求函数返回第k个最大元素,使tournamentTreeKSelection(data, 2)返回9。

我在修改算法以执行此任务时遇到了很多困难,因为我的假设是我必须跟踪max元素跳过的所有元素?任何帮助表示赞赏。

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

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

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

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

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

        if(k == 1) return tournamentTreeKSelection(listToArray(list),k);
        else return tournamentTreeKSelection(listToArray(list2), --k);
}

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

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

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


}

我现在修改了代码,但它只适用于k = 1 - 8,为什么它会崩溃? tournamentTreeKSelection(data,9)和tournamentTreeKSelection(data,10)返回3,它们应分别返回2和1。

1 个答案:

答案 0 :(得分:2)

首先,为什么你的代码错了:

  • 如果列表的大小为2或3,即使K > 1,您的语句min(data[i], data[i + 1])也会为真。

  • 你为什么要[10,1,9,2,8,3,7,4,6,5],我觉得你只想删除最大元素,但案例是什么 [1,1,2,2,3,3,4,4,5],在1次迭代后Math.max删除可能的结果9,8,7和6。

一些提示

  • 不做无用的计算。你正在计算这两个列表,虽然你在前面知道你只会使用其中一个。
  • 尽可能使用内置方法,请参阅Math.minArrayList
  • 请注意,您知道前面生成的数组的大小。无需创建k==1,这会给您带来很多开销。只需创建一个结果大小的数组。对于((data.length+1)/2)data.length-1其他K==1

仍然想知道

您说您的锦标赛树结构是必需的,但您在代码中循环它,因为它是一个数组。为什么?你可以从1循环中的public static int find(int[] a, int k) { int[] max = find(a, 0, a.length - 1, k); return max[k-1]; } private static int[] find(int[] a, int lo, int hi, int k) { if (hi < lo){ return new int[]{}; } if(lo == hi){ return new int[]{a[lo]}; } int mid = lo + (hi - lo) / 2; int[] left = find(a, lo, mid, k); int[] right = find(a, mid + 1, hi, k); return merge(left, right, k); } private static int[] merge(int[] left, int[] right, int k) { int[] res = new int[Math.min(k, left.length+right.length)]; int l = 0, r = 0; for (int i = 0; i<res.length;i++) { if (l == left.length) res[i] = right[r++]; else if (r == right.length) res[i] = left[l++]; else if (left[l] > right[r]) res[i] = left[l++]; else res[i] = right[r++]; } return res; } 那一刻确定最大值,而不是一次性地取一半的最大值。

替代方法

如前所述,可以使用排序方法或快速查找方法。我在想你怎么还能使用你的锦标赛树方法。我想出的最好的是合并排序是如何工作的。我稍微编辑了,因为你只需要返回最多K个元素。

{{1}}