快速排序和随机化快排学习

0.基本思想

选定基准值(通常为当前数组的第一个元素),经过一系列比较和交换,将比基准值小的放在其左边,将比基准值大的放在其右边,这称为是一次循环。

1. 递归1

 //百度百科上的C++实现:

#include <iostream>

using namespace std;

void Qsort(int arr[], int low, int high){
    if (high <= low) return;
    int i = low;
    int j = high + 1;
    int key = arr[low];
    while (true)
    {
        /*从左向右找比key大的值*/
        while (arr[++i] < key)
        {
            if (i == high){
                break;
            }
        }
        /*从右向左找比key小的值*/
        while (arr[--j] > key)
        {
            if (j == low){
                break;
            }
        }
        if (i >= j) break;
        /*交换i,j对应的值*/
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    /*中枢值与j对应值交换*/
    int temp = arr[low];
    arr[low] = arr[j];
    arr[j] = temp;
    Qsort(arr, low, j - 1);
    Qsort(arr, j + 1, high);
}

int main()
{
    int a[] = {9,8,7,6,5};

    Qsort(a, 0, sizeof(a) / sizeof(a[0]) - 1);/*这里原文第三个参数要减1否则内存越界*/

    for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
    {
        cout << a[i] << " ";
    }

    return 0;
}

 2.更简洁版本的代码

转自:https://blog.csdn.net/haelang/article/details/44496387,非常好的一篇博文! 

//快速排序
public static void QuickSort(int[] a, int left, int right) {
    if (left < right) {
        int p = partition(a, left, right);
        QuickSort(a, left, p - 1);
        QuickSort(a, p + 1, right);
    }
}

//快速排序数组划分
private static int partition(int[] a, int left, int right) {
    int x = a[right];
    int p = left - 1;
    for (int i = left; i < right; i++) {
        if (a[i] <= x) {//从小到大排序
            p++;
            swap(a, p, i);
        }
    }
    swap(a, p+1, right);
    return p+1;
}

精髓:p指向的是比基准元素(这里选取的是最右元素)小的最后一个的元素位置。最后p+1的位置就是right的最终的位置。

如果if判断语句改为了>,那么就是只有在a[i]比基准元素大的时候才会被调整到前面,就是大在前,从大到小排序了。

博文中给的例子也很好。

3.随机化快排

//快速排序的随机化版本,除了调用划分函数不同,和之前快排的代码结构一模一样
public static void RandomQuickSort(int[] a, int left, int right) {
    if (left < right) {
        int p = randomPartition(a, left, right);
        RandomQuickSort(a, left, p - 1);
        RandomQuickSort(a, p + 1, right);
    }
}

//随机化划分
public static int randomPartition(int[] a, int left, int right) {
    int r = random.nextInt(right - left) + left; //生成一个随机数,即是主元所在位置
    swap(a, right, r); //将主元与序列最右边元素互换位置,这样就变成了之前快排的形式。
    return partition(a, left, right); //直接调用之前的代码
}

因为在数组有序的时候,如果只选择最右作为基准,快排就退化为选择排序,复杂度O(n^2)。快排平均时间复杂度是O(nlogn),对于每一个元素都是排左和排右。所以这里的改进策略是随机选取基准元素,就可以得到平衡的划分。

4.性能比较

上面博文中给了测试结果,在随机化的序列中,普通快排相较于随机化更快一点,因为后者多了生成随机数与交换的过程。

但是在已经排好序的序列中,随机化是碾压级别的!

5.例题

应用:215. 数组中的第K个最大元素

原文地址:https://www.cnblogs.com/BlueBlueSea/p/10822029.html