Java 中位数算法工作不一致

Java 中位数算法工作不一致,java,arrays,algorithm,median-of-medians,Java,Arrays,Algorithm,Median Of Medians,我已经实现了中间值选择/中间值算法,使用以下内容作为参考,这已在此处链接 我的代码似乎适用于小数组~100,甚至适用于大小为100001答案5008的数组,但在大小为10001答案4960的输入数组上失败,我的代码输出4958 请注意,上述文本的正确答案相当于对数组进行排序并返回数组[array.length/2]处的元素,而不管数组大小是偶数还是奇数 我不知道如何调试这个问题。功能似乎是任意的,我只是迷路了。下面是我的代码: public class MedianOfMedians { pu

我已经实现了中间值选择/中间值算法,使用以下内容作为参考,这已在此处链接

我的代码似乎适用于小数组~100,甚至适用于大小为100001答案5008的数组,但在大小为10001答案4960的输入数组上失败,我的代码输出4958

请注意,上述文本的正确答案相当于对数组进行排序并返回数组[array.length/2]处的元素,而不管数组大小是偶数还是奇数

我不知道如何调试这个问题。功能似乎是任意的,我只是迷路了。下面是我的代码:

public class MedianOfMedians {

public static void main(String[] args) {
    MedianOfMedians mds = new MedianOfMedians();
    mds.run();
}

private void run() {
    Scanner in = new Scanner(System.in);
    int n = in.nextInt();
    int[] numArray = new int[n];
    for (int i = 0; i < n; i++) {
        numArray[i] = in.nextInt();
    }
    int median = select(numArray, numArray.length / 2);
    System.out.print(median);
}

private int select(int[] numArray, int k) {
    if (numArray.length <= 10) {
        int[] sorted = insertionSort(numArray);
        return sorted[k];
    }

    int divCount = (numArray.length % 5 == 0) ? numArray.length / 5 - 1 : numArray.length / 5;
    int[] medOfMed = new int[divCount + 1];
    int counter = 0;
    int[] subArray;

    while (counter <= divCount) {
        subArray = splitByFive(counter, divCount, numArray);
        medOfMed[counter] = select(subArray, subArray.length / 2);
        counter++;
    }

    int M = select(medOfMed, numArray.length / 10);

    List<Integer> lt = new ArrayList<>();
    List<Integer> eq = new ArrayList<>();
    List<Integer> gt = new ArrayList<>();
    for (int i : numArray) {
        if (i < M) {
            lt.add(i);
        } else if (i == M) {
            eq.add(i);
        } else {
            gt.add(i);
        }
    }
    if (k < lt.size()) {
        return select(createArray(lt), k);
    } else if (k > lt.size() + eq.size()) {
        return select(createArray(gt), k - lt.size() - eq.size());
    } else {
        return M;
    }
}

private int[] splitByFive(int splitIter, int divisions, int[] toSplit) {
    int numToCopy;
    if (splitIter == divisions) {
        numToCopy = toSplit.length - (5 * splitIter);
    } else {
        numToCopy = 5;
    }
    int[] subArray = new int[numToCopy];
    System.arraycopy(toSplit, splitIter * 5, subArray, 0, numToCopy);
    return subArray;
}

private int[] createArray(List<Integer> list) {
    int[] result = new int[list.size()];
    for (int i = 0; i < list.size(); i++) {
        result[i] = list.get(i);
    }
    return result;
}

private int[] insertionSort(int[] numArray) {
    for (int i = 1; i < numArray.length; i++) {
        int j = i;
        while (j - 1 >= 0 && numArray[j] < numArray[j - 1]) {
            int temp = numArray[j];
            numArray[j] = numArray[j - 1];
            numArray[j - 1] = temp;
            j--;
        }
    }
    return numArray;
}
}

我没有时间调试您的代码,但也许我可以为您提供一种调试技术,让您自己尝试一下,这对像这样的递归算法很有用

如果有一个算法失败的输入,正如你发现的那样,有一个最小的这样的输入——这个输入越小,就越容易找出哪里出了问题。因为该算法是递归的,所以您有一种很好的方法来隔离出第一个出错的地方:您可以使用一种缓慢、可信的方法来测试您将从select返回的结果是否正确,例如将数据复制到临时缓冲区,对其进行排序,然后在返回值之前抓取中途元素。如果您将函数重新安排为仅使用一条返回语句,则执行此操作会容易得多,例如:

private int select(int[] numArray, int k) {
    int knownCorrectAnswer = selectSlowlyButDefinitelyCorrectly(numArray, k);
    int willReturn;
    if (numArray.length <= 10) {
        int[] sorted = insertionSort(numArray);
        willReturn = sorted[k];    // Just remember what we will return
    } else {    // Need to add else branch here now

        ...

        if (k < lt.size()) {
            willReturn = select(createArray(lt), k);
        } else if (k > lt.size() + eq.size()) {
            willReturn = select(createArray(gt), k - lt.size() - eq.size());
        } else {
            willReturn = M;
        }
    }    // End of inserted else branch

    if (willReturn == knownCorrectAnswer) {
        return willReturn;
    } else {
        yell("First problem occurs with numArray=<...> and k=<...>!");
    }
}
yell应打印出整个问题实例,并通过抛出异常等方式停止程序。这种设置的好处在于,当调用yell时,已经完成的每个选择调用都是正确的,因为如果不是,yell可能已经被调用,程序也会在此时之前停止。因此,yell产生的输出保证是第一个,不一定是最小的,但通常也是出错的子问题