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


    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个最大元素

  • 相关阅读:
    UVA 1152 4 Values whose Sum is 0
    IOI 2006 Pyramid
    CTSC 2000 冰原探险
    IOI 2009 Mecho
    IOI 2011 Rice Hub 米仓
    NOIP 2013 火柴排队 程序
    USACO 2004 MooFest 奶牛集会
    USACO Training Section 3.1 Contact
    动态加载Ribbon功能区
    Ribbon2: 创建动态的Ribbon库
  • 原文地址:https://www.cnblogs.com/BlueBlueSea/p/10822029.html
Copyright © 2020-2023  润新知