Java中的Inplace Quicksort

时间:2015-04-13 16:02:24

标签: java quicksort

为了刷新一些Java,我试图实现一个可以对整数数组进行排序的快速排序(inplace)算法。以下是我到目前为止的代码。您可以通过sort(a,0,a.length-1)调用它。

如果两个'指针'这个代码明显失败(进入一个无限循环)。 i,j将每个指向与数据透视表值相同的数组条目。 pivot元素v始终是当前分区(索引最大的分区)的最右侧。

但我无法弄清楚如何避免这种情况,是否有人看到了解决方案?

static void sort(int a[], int left, int right)   {
    if (right > left){
        int i=left, j=right-1, tmp;
        int v = a[right]; //pivot
        int counter = 0;
        do {
            while(a[i]<v)i++;
            while(j>0 && a[j]>v)j--;

            if( i < j){
                tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
            }
        } while(i < j);
        tmp = a[right];
        a[right] = a[i];
        a[i] = tmp;
        sort(a,left,i-1);
        sort(a,i+1,right);

    }
}    

3 个答案:

答案 0 :(得分:3)

在预先形成Quicksort时,我强烈建议使用单独的分区方法来使代码更容易理解(我将在下面显示一个示例)。除此之外,避免最坏情况运行时间的好方法是在执行快速排序之前对您排序的阵列进行排序。我还使用第一个索引作为分区项而不是最后一个。

例如:

public static void sort (int[] a)
{
    StdRandom.shuffle(a);
    sort(a, 0, a.length - 1);
}

private static void sort(int[] a, int lo, int hi)
{
    if (hi <= lo) return;
    int j = partition(a, lo, hi) // the addition of a partitioning method
    sort(a, lo, j-1);
    sort(a, j+1, hi);
}

private static int partition(int[] a, int lo, int hi)
{
    int i = lo, j = hi + 1, tmp = 0;
    int v = a[lo];
    while (true)
    {
         while (a[i++] < v) if (i == hi) break;
         while (v < a[j--]) if (j == lo) break;
         if (i >= j) break;
         tmp = a[i];
         a[i] = a[j];
         a[j] = tmp;
    }
    tmp = a[lo];
    a[lo] = a[j];
    a[j] = temp;
    return j;
}

除此之外,如果你想要一个关于Quicksort如何工作的非常好的例子(作为复习),请参阅here

答案 1 :(得分:2)

这应该有效(将检查一点的正确性,它可以正常工作!):

编辑:我以前在错误检查中犯了一个错误。我忘了添加2个条件,这里是修改后的代码。

public static void main (String[] args) throws java.lang.Exception
{
    int b[] = {10, 9, 8, 7, 7, 7, 7, 3, 2, 1};
    sort(b,0,b.length-1);
    System.out.println(Arrays.toString(b));
}

static void sort(int a[], int left, int right)   {  
   if (right > left){
    int i=left, j=right, tmp;    
    //we want j to be right, not right-1 since that leaves out a number during recursion

    int v = a[right]; //pivot

    do {
        while(a[i]<v)
          i++;
        while(a[j]>v) 
        //no need to check for 0, the right condition for recursion is the 2 if statements below.
          j--;

        if( i <= j){            //your code was i<j
           tmp = a[i];
           a[i] = a[j];
           a[j] = tmp;
           i++;            
           j--;
           //we need to +/- both i,j, else it will stick at 0 or be same number
        }
   } while(i <= j);           //your code was i<j, hence infinite loop on 0 case

    //you had a swap here, I don't think it's needed.
    //this is the 2 conditions we need to avoid infinite loops
    // check if left < j, if it isn't, it's already sorted. Done

    if(left < j)  sort(a,left,j);
    //check if i is less than right, if it isn't it's already sorted. Done
    // here i is now the 'middle index', the slice for divide and conquer.

    if(i < right) sort(a,i,right);
  }

}

This Code in the IDEOne online compiler

基本上我们确保如果i / j的值与pivot相同,我们也交换该值,并且突破递归。

还有一个检查长度的伪代码,好像我们有一个只有1个项目的数组它已经排序了(我们忘记了基本情况),我想我们需要但是因为你传入索引和整个数组,而不是子数组,我们只是增加i和j,所以算法不会坚持0(他们做了排序)但仍然继续排序数组1. :)

另外,我们必须添加2个条件来检查数组是否已经为递归调用排序。没有它,我们将永远排序已排序的数组,因此另一个无限循环。看看我如何添加检查,如果留下小于j,如果我小于正确。此外,在传递i和j的那一点上,我实际上是我们为分而治之分裂的中间指数,而j将是中间值之前的值。

它的伪代码取自RosettaCode

function quicksort(array)
    if length(array) > 1
        pivot := select any element of array
        left := first index of array
        right := last index of array
        while left ≤ right
            while array[left] < pivot
                left := left + 1
            while array[right] > pivot
                right := right - 1
            if left ≤ right
                swap array[left] with array[right]
                left := left + 1
                right := right - 1
        quicksort(array from first index to right)
        quicksort(array from left to last index)

参考:此SO question

另请阅读this for a quick refresher, it's implemented differently with an oridnary while loop

这很有趣:)

答案 2 :(得分:0)

Heres some simple code I wrote that doesn't initialize to many pointers and gets the job done in a simple manner.

public int[] quickSort(int[] x ){
    quickSortWorker(x,0,x.length-1);
    return x;
}


private int[] quickSortWorker(int[] x, int lb, int ub){
    if (lb>=ub) return x; 
    int pivotIndex = lb;
    for (int i = lb+1 ; i<=ub; i++){
        if (x[i]<=x[pivotIndex]){
            swap(x,pivotIndex,i);
            swap(x,i,pivotIndex+1);
            pivotIndex++;
        }
    }
    quickSortWorker(x,lb,pivotIndex-1);
    quickSortWorker(x,pivotIndex+1,ub);
    return x;
}

private void swap(int[] x,int a, int b){
    int tmp = x[a];
    x[a]=x[b];
    x[b]=tmp;
}