• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • 算法鋪子之排序---快速排序(一)

    0.快速排序簡介

    快速排序是20世紀世界上最偉大的算法之一,顧名思義,這個算法能很快的對數據進行排序。而且在很多庫的底層代碼中也經常使用快速排序來實現排序的功能,比如jdk,stl等。

    1.快速排序的思想

    以升序為例。首先會找一個元素v作為基準,然后將整個待排序序列parttion成3個區域A,B和C。其中A區域為全部比v小的元素,B區域為全部等于v的元素,C區域為全部大于v的元素。此時B區域就成為了元素v在序列中的最終位置,也就是說下一趟排序的時候不需要考慮元素v了,然后將元素v的位置p記錄下來。然后根據分治法的思想將整個待排序序列一分為二,即p左邊一個部分和p右邊一個部分,一直這樣遞歸地二分下去,直到二分出來的區域只有1個元素的時候終止遞歸。就這樣每一次parttion之后,所選擇的基準元素都被置換到了整個序列有序時的最終位置。

    parttion所做的工作就是將整個序列以元素v為基準,逐漸將整個序列調整成兩個區域,一個區域中的元素小于v,另一個區域中的元素大于v。而v所在的位置為分界點。parttion的工作流程我們用下面的圖來表示:
    這里寫圖片描述
    其中橙色區域中的元素都小于v,紫色區域中的元素都大于v。l為基準元素v的索引,j為橙色區域的右閉區間的索引,i為當前待檢查的元素的索引,同時i-1就是紫色區域的右閉區間的索引,e表示當前待檢查的元素的值。

    那么當e大于v的時候,要做的事情就很簡單,只要++i就行了。那如果e小于v的話,為了保持序列只有橙色和紫色區域的性質,需要將e扔到橙色區域里面去。為了實現這個功能,可以先將arr[j+1]和a[i]進行交換,然后++j,然后++i繼續檢查下一個元素。
    這里寫圖片描述
    這里寫圖片描述
    在序列中的所有元素都遍歷完成之后,只要將arr[l]和arr[j]進行交換,就能得到parttion的最終結果了。
    這里寫圖片描述
    這里寫圖片描述

    2.快速排序的實現

    template<typename T>
    int parttion(vector<T>& data, int left, int right)
    {
        //將整個數組劃分成小于key key 大于key三個部分,j表示小于key部分的最后一個索引
        T key = data[left];         //基準值
        int l = left;               //基準值的索引 
        int j = left;               
        for (int i = left + 1; i <= right; ++i)
        {
            if (data[i] < key)
            {
                swap(data[i], data[++j]);
            }
        }
        swap(data[j], data[l]);
    
        return j;
    }
    
    template<typename T>
    void quickSortKernel(vector<T>& data, int left, int right)
    {
        if (left >= right)
            return;
        int p = parttion(data, left, right);
        quickSortKernel(data, left, p-1);
        quickSortKernel(data, p + 1, right);
    }
    
    template<typename T>
    void quickSort(vector<T>& data)
    {
        quickSortKernel(data, 0, data.size() - 1);
    }

    3.分析

    3.1 隨機數據實驗

    本實驗使用隨機生成的1000000數據作為輸入,使用上一篇中優化后的歸并排序和第2節中實現的快速排序進行對比,實驗結果如下:
    這里寫圖片描述

    從圖可以看出在數據的有序程度不高的情況下,同是O(nlogn)的歸并排序算法的速度沒有快速排序高。

    3.2 近似有序數據實驗

    本實驗使用隨機生成的1000000近似有序數據作為輸入,使用上一篇中優化后的歸并排序和第2節中實現的快速排序進行對比,實驗結果如下:

    這里寫圖片描述

    臥槽。。快速排序用了300多秒。。這還是快速排序么。。下面來分析原因。

    歸并排序之所以是O(nlogn)的排序算法,是因為會將待排序列一分為二,然后再一分為二,以此類推。那么整個層數就是logn的,合并的時候是n的,所以是O(nlogn)的算法。
    這里寫圖片描述
    而快速排序算法也是一分為二,逐步劃分的過程,但快速排序二分出來的子序列可能是一大一小,而不是正好的一半一半。
    這里寫圖片描述
    如果在最壞的情況下(完全有序的情況),快速排序二分時的二叉樹也就退化成了鏈表,也就是說退化成了O(n^2)的算法。
    image
    為了解決這個退化問題,可以在選定基準的時候進行隨機選擇,從概率上來講,對近乎有序的數據使用快速排序時隨機選擇基準,而不是固定選擇最左邊的元素為基準的話,退化成O(n^2)的概率幾乎為0。(退化的概率=1/n*1/(n-1)*1/(n-2)…*1)

    那么優化的代碼如下:

    template<typename T>
    int parttion(vector<T>& data, int left, int right)
    {
        //隨機選取基準
        int randIndex = rand() % (right - left + 1) + left;
        T key = data[randIndex];
        swap(data[left], data[randIndex]);
    
        int l = left;               //基準值的索引 
        int j = left;               
        for (int i = left + 1; i <= right; ++i)
        {
            if (data[i] < key)
            {
                swap(data[i], data[++j]);
            }
        }
        swap(data[j], data[l]);
    
        return j;
    }

    經過優化之后,實驗結果如下:
    這里寫圖片描述

    可以看到經過這么一個簡單的優化之后,得到了質的飛躍,從O(n^2)又回到了O(nlogn),簡直美滋滋。

    3.3 擁有較多重復數據實驗

    本實驗使用隨機生成的1000000有大量重復數據的數據作為輸入,使用上一篇中優化后的歸并排序和第2節中實現的快速排序進行對比,實驗結果如下:

    這里寫圖片描述

    吃驚!又是這么慢!!這是因為如果待排序序列中有大量的重復數據的話,那么很有可能造成一種結果,就是橙色區域和紫色區域不是那么平衡!這樣也就是說,很有可能同樣的退化成一個O(n^2)的算法。

    這里寫圖片描述

    為了解決這種問題,算法大佬們提出了雙路快速排序算法和三路快速排序算法,使得快速排序算法在各種特點的數據集中的表現都是接近O(nlogn)的。至于具體的算法思路和實現,將在下一篇博客中進行分析。

    版權聲明:本文為alw_123原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/alw_123/article/details/76167621

    智能推薦

    排序算法之快速排序

      快速排序的基本思想:通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。 1、算法描述   快速排序使用分治法來把一個串(list)分為兩個子串(sub-lists)。具體算法描述如下: 1. i =L; j = R; 將基準數挖出形成第一個坑a[i]; 2. j...

    排序算法之快速排序

    快速排序算法的原理如下: 1、首先設定一個分界值,通過該分界值將數組分成左右兩部分。 2、將大于或等于分界值的數據集中到數組右邊,小于分界值的數據集中到數組的左邊。此時,左邊部分中各元素都小于或等于分界值,而右邊部分中各元素都大于或等于分界值。  3、然后,左邊和右邊的數據可以獨立排序。對于左側的數組數據,又可以取一個分界值,將該部分數據分成左右兩部分,同樣在左邊放置較小值,右邊放置較大...

    排序算法之快速排序

    1. 基本思想 任取待排序元素序列中的某個元素作為基準值,按照該排序碼將待排序序列分割為兩個子序列,其左子序列中所有元素均小于基準值,其右子序列中所有元素均大于基準值,然后將其左右子序列按照上述過程重復操作,直到所有元素均排列在相應位置上為止。 操作的基本過程:若規定基準值key始終為待排序序列的最后一個元素,定義兩個索引left和right,left將在待排序序列中從前往后找到第一個大于key的...

    排序算法之快速排序

        之前寫過了快速排序的算法(快速排序),這里寫的與之前的稍有不同,目的主要是為了歸納《算法設計技巧與分析》這本書中的所有算法的代碼實現,所以不遺余力的再次總結一下。    偽代碼和算法的效率分析直接貼圖 劃分偽代碼(找出快速排序的w位置,使得w左邊所有的數小于它,w右邊所有的數大于它) 排序偽代碼(調用SPLIT劃分算法) 效率分析...

    猜你喜歡

    排序算法之 - 快速排序

    快速排序的第一步是在列表中隨機選取一個 基準值,將列表中剩余的其他數與基準值 逐一比較,小于基準值的放在基準值左側,大于基準值的放在基準值右側,這樣就會形成如下的新列表: [小于基準值的列表] + 基準值 + [大于基準值的列表] 接下來對左側小于基準值的列表繼續執行上述過程,直至最后左側排序完成 再對右側大于基準值的列表繼續執行上述過程,直至右側排序完成 Notice:在將數字分別放到基準值左右...

    排序算法之快速排序

    排序算法之快速排序 簡介 原理 樞紐元的選取 分割策略 代碼實現 性能分析 簡介 今天介紹排序算法中最重要的快速排序,顧名思義,快速排序之所以能在歷史的長河中脫穎而出以“快速”兩字命名,就是因為經多年實踐證明它是已知最快的泛型排序算法。并且到目前為止還沒有哪個算法能撼動其位置。所以它的重要性是不言而喻的,是我們一定要熟悉并掌握的排序算法。 原理 前不久我們剛講過歸并排序(歸...

    排序算法之快速排序

    快速排序 (QuickSorting) 是對冒泡排序的一種改進,由 C.A.R.Hoare 在 1962 年提出。 它的基本思想是: 通過一趟排序將要排序的數據分割成獨立的兩部分, 其中一部分的所有數據都比另外一部分的所有數據都要小, 然后再按此方法對這兩部數據分別進行快速排序, 整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。     1、先從數列中取出一個數作為基準...

    排序算法之快速排序

    快速排序原理: 1.用最左側元素作“樞軸”,存入“哨所” 2.設low和high指向兩端 3.high向左移動,一旦遇到小于樞軸的元素,則將其移動到左側,放入low指向的位置; 4.low向右移動,一旦遇到大于樞軸的元素,則將其移動到右側,放入high指向的位置; high和low從兩側交替向中間移動,直至low=high. 左側均小于等于樞軸,右側...

    排序算法之快速排序

    基本原理 找出一個元素(理論上可以隨便找一個)作為基準,通過一趟排序將要排序的數據分割成獨立的兩部分, 其中一部分的所有數據都比這個基準小,另外一部分的所有數據都比這個基準大,然后再按此方法對這 兩部分數據分別進行快速排序,所以快速排序算法的核心算法就是分區操作,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。 一趟快速排序算法 設置兩個變量i,j,排序開始的時候:i=0,j=N-1 找一...

    精品国产乱码久久久久久蜜桃不卡