Quicksort 实现:使用随机枢轴时几乎已排序

Quicksort implementation: nearly sorted when using random pivot

我正在努力实现快速排序,作为我自己的一些练习和复习。目前这只是原始整数数组的简单实现。得到这个工作正常后,我打算使它通用。

我下面的内容仍然有一个小错误,我无法找到。我最初写它是为了简单地使用左索引作为基准,一切正常。然而,一旦我完成编写并将其切换为使用随机枢轴,事情就不再完全正确了。我的测试数组几乎已排序,但仍有一些元素距离它们应有的位置有两到三个索引。

以下是我的一些测试用例,这些用例在 运行 时展示了不正确的行为:

6, 5, 1, 3, 8, 4, 7, 9, 2

1, 2, 3, 4, 5

5, 4, 3, 2, 1

 /**
 * Sort an integer array using quicksort
 * 
 * @param a Array to sort
 */
public static void quicksort(int[] a) {
    partition(0, a.length - 1, a);
}

/**
 * Partition a section of an integer array around a randomly selected pivot within that section
 *
 * @param left Lower-bound index of section (inclusive)
 * @param right Upper-bound index of section (inclusive)
 * @param a Array to perform partitioning within
 */
private static void partition(int left, int right, int[] a) {
    // Exit if partition is only a single element
    if (right - left < 1) { return; }

    // Select a pivot at random
    int pivot = ThreadLocalRandom.current().nextInt(left, right + 1);

    // Move pivot to left-most position (get out of the way)
    swap(left, pivot, a);

    // Perform partitioning
    int cur = left + 1;
    for (int i = left + 1; i <= right; i++) {
        if (a[i] < a[pivot]) {
            swap(i, cur, a);
            cur++;
        }
    }

    // Put pivot back where it belongs
    swap(left, cur - 1, a);

    // Partition the two new partitions
    partition(left, cur - 2, a);
    partition(cur, right, a);
}

/**
 * Swaps two elements in an array of integers
 *
 * @param i Index of first element to swap
 * @param j Index of second element to swap
 * @param a Integer array to perform swap on
 */
private static void swap(int i, int j, int[] a) {
    int temp = a[i];
    a[i] = a[j];
    a[j] = temp;
}

好的,尝试使用左而不是枢轴。它将反转列表的排序。

if (a[i] > a[left]) {
            swap(i, cur, a);
            cur++;
}

您没有考虑更改。

我更习惯于向右旋转。我让你的实现很小。我的问题是,第一个右边必须 > 左边,并且可能需要快速排序分区 (left,cur-1,a)。

交换的位置也应该已经排序。尽管您以正确的基础方法来考虑这一点。

partition(left, cur -1 , a);
partition(cur + 1, right, a);

您可以在 Wikipedia

阅读有关实施的信息

我的代码在编码场运行。

public class HelloWorld{

   int[] a = null;



    /**
 * Sort an integer array using quicksort
 * 
 * @param a Array to sort
 */
public void quicksort() {
    partition(0, a.length - 1, a);
}

/**
 * Partition a section of an integer array around a randomly selected pivot within that section
 *
 * @param left Lower-bound index of section (inclusive)
 * @param right Upper-bound index of section (inclusive)
 * @param a Array to perform partitioning within
 */
private  void partition(int left, int right, int[] a) {
    // Exit if partition is only a single element
    if (right <= left || right - left == 0) { return; }

    // Select a pivot at random
    int pivot =  left + new java.util.Random().nextInt(right - left);
    pivot = right;

    // Move pivot to left-most position (get out of the way)
    swap(right, pivot, a);

    // Perform partitioning
    int cur = left;
    for (int i = left; i < right; i++) {
        if (a[i] <= a[right]) {
            swap(i, cur, a);
            cur++;
        }
    }

    // Put pivot back where it belongs
    swap(cur, right , a);

    // Partition the two new partitions
    partition(left, cur -1 , a);
    partition(cur + 1, right, a);
}

/**
 * Swaps two elements in an array of integers
 *
 * @param i Index of first element to swap
 * @param j Index of second element to swap
 * @param a Integer array to perform swap on
 */
private void swap(int i, int j, int[] a) {
    int temp = a[i];
    a[i] = a[j];
    a[j] = temp;
}


     public static void main(String []args){
         int[] arr = new int[]{0,4,2,3,9,11,20,100,50,32,45,27,13,2,1,4,99,1,4};
         HelloWorld hw = new HelloWorld();
         hw.a = arr;
         hw.quicksort();
         for(int i = 0;i< arr.length;i++){
             System.out.println(arr[i]);
         }
     }
}

这里是枢轴在左边的代码。

public class HelloWorld{

   int[] a = null;



    /**
 * Sort an integer array using quicksort
 * 
 * @param a Array to sort
 */
public void quicksort() {
    partition(0, a.length - 1, a);
}

/**
 * Partition a section of an integer array around a randomly selected pivot within that section
 *
 * @param left Lower-bound index of section (inclusive)
 * @param right Upper-bound index of section (inclusive)
 * @param a Array to perform partitioning within
 */
private  void partition(int left, int right, int[] a) {
    // Exit if partition is only a single element
    if (right <= left || right - left == 0) { return; }

    // Select a pivot at random
    int pivot =  left + new java.util.Random().nextInt(right - left);


    // Move pivot to left-most position (get out of the way)
    swap(left, pivot, a);

    // Perform partitioning
    int cur = left + 1;
    for (int i = left +1 ; i <=  right; i++) {
        if (a[i] < a[left]) {
            swap(i, cur, a);
            cur++;
        }
    }


    // Put pivot back where it belongs
    swap(cur - 1 , left , a);

    // Partition the two new partitions
    partition(left, cur - 2 , a);
    partition(cur , right, a);
}

/**
 * Swaps two elements in an array of integers
 *
 * @param i Index of first element to swap
 * @param j Index of second element to swap
 * @param a Integer array to perform swap on
 */
private void swap(int i, int j, int[] a) {
    int temp = a[i];
    a[i] = a[j];
    a[j] = temp;
}


     public static void main(String []args){
         int[] arr = new int[]{6, 5, 1, 3, 8, 4, 7, 9, 2};
         HelloWorld hw = new HelloWorld();
         hw.a = arr;
         hw.quicksort();
         for(int i = 0;i< arr.length;i++){
             System.out.println(arr[i]);
         }
     }
}

正在将我的评论转化为答案:

在这部分代码中,您将枢轴元素与最左边的元素交换:

// Select a pivot at random
int pivot = ThreadLocalRandom.current().nextInt(left, right + 1);

// Move pivot to left-most position (get out of the way)
swap(left, pivot, a);

但是,您实际上并没有更改数据透视元素的索引。这意味着在此分区逻辑中,您正在查看数组中的错误索引:

for (int i = left + 1; i <= right; i++) {
    if (a[i] < a[pivot]) {
        swap(i, cur, a);
        cur++;
    }
}