我的快速排序实现不适用于10000个序列号

时间:2017-03-31 09:56:58

标签: java algorithm

我有一个快速排序实现,这种排序实现与小数组和10000个随机数一起使用,但当输入为10000个序列号(从1到10000)时,它会抛出堆栈溢出错误

public class QuickSort<T extends Comparable> extends Sort<T>{

public void sort(Comparable[] input) {
    sort(input, 0, input.length-1);
}

private void sort(Comparable[] a, int lo, int hi) {
    if (hi <= lo) return;

    int j = partition(a, lo, hi);
    sort(a, lo, j-1);
    sort(a, j+1, hi);
}

private int partition(Comparable[] a, int lo, int hi) {
    int i=lo;
    int j=hi + 1;
    Comparable v = a[lo];

    while(true) {
        while (less(a[++i], v)) {
            if (i == hi) break;
        }

        while (less(v, a[--j])) {
            if (j == lo) break;
        }

        if(i >= j) break;

        exch(a, i, j);
    }
    exch(a, lo, j);
    return j;
}

public static boolean less (Comparable a, Comparable b) {
    return a.compareTo(b) < 0;
}

public static void exch(Comparable[] array, int i, int j) {
    Comparable temp = array[i];
    array[i] = array[j];
    array[j] = temp;
}

public static void main(String...args) {
    Integer[] array = new Integer[] {10,2,9,11,1,19,9,4,6,2,1,4,5,6};
    new QuickSort<>().sort(array);

    for (int temp : array) {
        System.out.print(temp + " ");
    }
}
}

它适用于10000个随机数和其他输入。但是当使用10000个序列号(从1到10000)执行时抛出stackoverflow错误

2 个答案:

答案 0 :(得分:3)

简单Quicksort实现在最坏情况下具有O(n ^ 2)复杂度和O(n)额外内存要求。由于枢轴元素选择方法不好,您在有序序列上遇到了这种最坏情况。

Wikipedia

  

在quicksort的早期版本中,最左边的元素   通常会选择分区作为枢轴元素。不幸,   这导致已经排序的数组的最坏情况行为,这是一个   相当常见的用例。通过选择可以轻松解决问题   或者是枢轴的随机索引,选择中间索引   分区或(尤其是对于较长的分区)选择中位数   枢轴分区的第一个,中间和最后一个元素(如   Sedgewick推荐。

解决此问题的简单方法是将中间元素作为数据透视表。取代

Comparable v = a[lo];

Comparable v = a[lo+(hi-lo)/2];

为此透视选择方法创建最坏情况测试并不困难,但您需要在大输入情况下有意识地进行此操作。如果你想要排序算法类似于Quicksort并且没有O(n ^ 2)最坏情况,你应该看看Introsort算法。

答案 1 :(得分:0)

发生堆栈溢出是因为您确实尝试使用更多堆栈空间,然后可用于JVM中的Java程序。

这是因为您的数据透视选择策略只是选择第一个元素作为数据透视表,这会在数组已经排序时导致最坏情况下的性能 - 在这种情况下算法运行时间为O(n ^ 2)和O(n)空间 - 这意味着递归是10000深,使用的堆栈比可用的多。

您有几个选择:

  1. 增加堆栈空间 - 512k的默认堆栈大小(在大多数JVM中),但您可以使用# Lets check if any of the results contain failure $failedChecks = $results | Where-object{$_.'Last Result' -ne 0} # Evaluate $failedChecks as a boolean. If there are no failed checks Else can be assumed that everything is fine. if($failedChecks){ Write-Log "These domain controllers have replication errors. Please review them..." -Level Error $error = $failedChecks | select 'Source DC','Partner DC','Direction' | ft -AutoSize | Out-String Write-Log $error -Level Error ExitWithCode -exitcode 2 } else { Write-Log "There were no replication issues found" -Level Info ExitWithCode -exitcode 0 } 标志更改此值。在我的环境中,当我添加标志-Xss(18 MB)时,我可以使用200k的数组运行程序而不会发生堆栈溢出。你可以增加它甚至更多。

  2. 尝试使用更少的堆栈空间 - 递归和函数中定义的堆栈变量使用堆栈空间。

    例如,您可以将数组本身更改为类-Xss18m的成员,而不是将其作为参数传递。这将在每次调用中仅向堆栈添加两个整数(lo和hi),并节省堆栈空间。

  3. (最佳选择) - 使用随机数据 - 选择QuickSortlo之间的随机元素作为支点,而不是第一个。 而不是

    hi

  4. 使用

    Comparable v = a[p];

    最后

     int p = lo + r.nextInt(hi  - lo);
     Comparable v = a[p];