• 常见排序算法-----快速排序


    最好时间复杂度O(nlogn) 最坏时间复杂度O(N^2)

    方法一:

    左右指针法:

     1     /**
     2      * 双指针法,将基准点设置为最左端
     3      * 
     4      * @param arr
     5      * @param left
     6      * @param right
     7      */
     8     public static void quicklySort(int[] arr, int left, int right) {
     9 
    10         if (left >= right) {
    11             return;
    12         }
    13 
    14         int base = arr[left];
    15         int i = left;
    16         int j = right;
    17         while (i < j) {
    18             while (j > i && arr[j] >= base) {
    19                 j--;
    20             }
    21             while (j > i && arr[i] <= base) {
    22                 i++;
    23             }
    24             if (i < j) {
    25                 int temp = arr[i];
    26                 arr[i] = arr[j];
    27                 arr[j] = temp;
    28             }
    29         }
    30         arr[left] = arr[i];
    31         arr[i] = base;
    32         quicklySort(arr, left, i - 1);
    33         quicklySort(arr, i + 1, right);
    34 
    35     }

    当基点设置在最左端时,要让右指针先移动(当基准设置在最右端时,要让左指针先移动)

    S1 右指针移动找到第一个比基准小的数停止.

    S2 左指针开始移动,找到第一个比基准大的数停止

    S3 将左右指针所指的数进行交换

    S4 继续上述步骤直到左右指针相遇,将其与基准进行交换

    S5 按照基准的位置,分为左右两侧分别进行上述步骤,直到拆分到只剩一个元素 排序完毕

    当基点设置在最左端时,要让右指针先移动(当基准设置在最右端时,要让左指针先移动)原因:

    因为右指针最后停止的时候会和左指针发生交换,导致右指针停止的时候所指向的数要大于基准

    若此时让左指针先进行移动,最后和两个指针相遇 (相遇在右指针的位置,此时右指针指向的数要大于基准)
    若基准设置在最左端,会导致右指针指向的大于基准的数移动到左端,导致左端不是全部小于基准的数,排序失败

    右指针先移动同理。

    所以,当基准在最左端的时候,要右指针先移动,基准在最右端时,要左指针先移动。

    方法二 挖坑法 (基准设在左边 右指针先走 和上面一样)

     1     // 挖坑法
     2     public static void quicklySort2(int[] arr, int left, int right) {
     3 
     4         if(left >=right){
     5             return ;
     6         }
     7         
     8         int base = arr[left];
     9         int i = left;
    10         int j = right;
    11 
    12         while (i < j) {
    13             // 右指针先走
    14             while (j > i && arr[j] >= base) {
    15                 j--;
    16             }
    17             arr[i] = arr[j];
    18 
    19             // 左指针再走
    20             while (j > i && arr[i] <= base) {
    21                 i++;
    22             }
    23             arr[j] = arr[i];
    24         }
    25         arr[j] = base;
    26         quicklySort2(arr, left, j - 1);
    27         quicklySort2(arr, j + 1, right);
    28 
    29     }

    取基准的几种方法

    详细见https://blog.csdn.net/hacker00011000/article/details/52176100

    (1)固定位置  

    (2)随机选取基准, 选取后 将该位置的基准与最左端进行交换,即可以用常规的快排方法进行排序

    (3)三数去中法。使用最左端,最右端和中间位置的三个元素,将这个三个元素进行排序,选取中间位置的数做为基准数,将其与最左端进行交换,即可用普通的快排进行处理。

     四种优化方法

    优化1:当待排序序列的长度分割到一定大小后,使用插入排序

    优化2:在一次分割结束后,可以把与Key相等的元素聚在一起,继续下次分割时,不用再对与key相等元素分割

    具体过程:在处理过程中,会有两个步骤 
    第一步,在划分过程中,把与key相等元素放入数组的两端 
    第二步,划分结束后,把与key相等的元素移到枢轴周围

    void QSort(int arr[],int low,int high)  
    {  
        int first = low;  
        int last = high;  
    
        int left = low;  
        int right = high;  
    
        int leftLen = 0;  
        int rightLen = 0;  
    
        if (high - low + 1 < 10)  
        {  
            InsertSort(arr,low,high);  
            return;  
        }  
    
        //一次分割  
        int key = SelectPivotMedianOfThree(arr,low,high);//使用三数取中法选择枢轴  
    
        while(low < high)  
        {  
            while(high > low && arr[high] >= key)  
            {  
                if (arr[high] == key)//处理相等元素  
                {  
                    swap(arr[right],arr[high]);  
                    right--;  
                    rightLen++;  
                }  
                high--;  
            }  
            arr[low] = arr[high];  
            while(high > low && arr[low] <= key)  
            {  
                if (arr[low] == key)  
                {  
                    swap(arr[left],arr[low]);  
                    left++;  
                    leftLen++;  
                }  
                low++;  
            }  
            arr[high] = arr[low];  
        }  
        arr[low] = key;  
    
        //一次快排结束  
        //把与枢轴key相同的元素移到枢轴最终位置周围  
        int i = low - 1;  
        int j = first;  
        while(j < left && arr[i] != key)  
        {  
            swap(arr[i],arr[j]);  
            i--;  
            j++;  
        }  
        i = low + 1;  
        j = last;  
        while(j > right && arr[i] != key)  
        {  
            swap(arr[i],arr[j]);  
            i++;  
            j--;  
        }  
        QSort(arr,first,low - 1 - leftLen);  
        QSort(arr,low + 1 + rightLen,last);  

    优化3:优化递归操作

    快排函数在函数尾部有两次递归操作,我们可以对其使用尾递归优化

    优点:如果待排序的序列划分极端不平衡,递归的深度将趋近于n,而栈的大小是很有限的,每次递归调用都会耗费一定的栈空间,函数的参数越多,每次递归耗费的空间也越多。优化后,可以缩减堆栈深度,由原来的O(n)缩减为O(logn),将会提高性能。

    优化4:使用并行或多线程处理子序列

  • 相关阅读:
    JavaScript在Javascript中为String对象添加trim,ltrim,rtrim方法
    JavaScriptjs写的俄罗斯方块
    WinForm中“嵌入的资源”和“资源文件”数据的获取方式
    Facade模式(外观模式)
    windows服务安装程序中如何安装后自动启动
    水晶报表之主从多表数据源批量预览及打印开发设计
    IP地址分类简介
    水晶报表之各节的作用
    水晶报表开发之常用代码以及注意事项
    .Net中后台线程和前台线程的区别
  • 原文地址:https://www.cnblogs.com/maxbolg/p/9355151.html
Copyright © 2020-2023  润新知