深入理解快速排序與快速排序的優化

2021-09-25 02:40:09 字數 3659 閱讀 9502

1. 寫在前面

2. 文章結構

主要講解一路快排,二路快排和三路快排的實現過程,作圖略。對於快排的複雜度以及其它背景知識可自行學習。

3. 關於快排

過程:選擇乙個參考點,以從小到大排序為例,那麼在一次快排之後,在參考點左側的資料都比參考點小,在參考點右側的資料都比參考點大,每次快排之後的參考點也就是分割點。這個過程如何做到呢?我們選擇乙個參考點,將它置於整個陣列的左端(右端),在掃瞄的過程中,把比參考點大的資料放在參考點右側,比參考點小的資料放在參考點左側。

思路:由於快排是典型的分治演算法,那就與歸併排序的思路相同,我們將採用遞迴的方法解決問題,當整個問題的所有子問題都被解決時,原問題也就得到了解決。

解決問題的

關鍵:找出快速排序演算法的分割點(partition),用以將原問題劃分成若干子問題,再對每個子問題分別使用快速排序,最後解決問題,與歸併排序不同的是,它並不是每次都將陣列分割成大小相同的兩個部分,一方面,參考點的選擇會極大影響演算法的效率,另一方面,這也為我們的優化提供了可能。

對於參考點的選擇是隨機的,只是在二路快排中參考點所處的位置對開始掃瞄的方向有些約束,這是由於排序規則(從小到大排序還是從大到小排序)限定的,一般情況下,我們選擇陣列第乙個元素為參考點,若要選擇陣列最後乙個元素為參考點,則要注意二路快排**中每次快排優先掃瞄的方向。示例**均採用從小到大的順序進行編寫

4.一路快排

首先,選取陣列第乙個元素作為參考點,此外,我們需要幾個指標(陣列下標,下同),指標lo和hi,表示每個子陣列的低位和高位,指標i指向當前正在被考察的元素,顯然,對於i有i∈[lo,hi],由3我們知道,在每次快排的過程中,我們需要確定分割點的位置,我們設這個指標為j。很顯然,此時陣列被分割成三個部分:從lo到j,當前正在考察的元素i,從i+1到hi。當正在考察的元素比參考點小時,我們將它與j+1位置的元素交換位置。因為:j表示的是參考點的位置,由3我們知道j+1位置的元素一定是大於參考點的,經過這次交換,有乙個比參考點小的元素跑到參考點後乙個位置,此時,只需要對參考點的指標+1即可,這相當於小於參考點的區域增加了乙個元素,而對於比參考點大的元素,則跑到了陣列右端,滿足題設。隨著陣列的遍歷的進行,參考點j表示的下標也不斷更新,當整個陣列遍歷完畢時,指標j指向的就是參考點的位置,最後,我們交換參考點的資料與j指向的資料,一次快排就完成了。

package com.night.baoly.sort;

public class onewaysort ;

quicksort(arr, 0, arr.length - 1);

for (int i : arr)

} private static void quicksort(int arr, int lo, int hi)

private static int getpartition(int arr, int lo, int hi)

} swap(arr, lo, j);

return j;

} private static void swap(int arr, int a, int b)

}

5 二路快排

為什麼需要二路快排? 因為在處理近乎有序的陣列時,一路快排的速度會大大降低(因為一路快排的分割特點,導致分割有序陣列時將會產生高度不平衡的二叉樹結構,使得演算法的時間複雜度退化成o(n2))

處理過程二路快排是指有兩個指標,我們設它們分別為left和right,從陣列兩端分別開始掃瞄,left指標從左向右尋找比參考點大的元素,right指標從右想左尋找比參考點小的元素,當兩個指標出現重疊或交叉時,參考點的位置就確定了。在這個過程中,如果左右指標都找到了合適的元素而沒有出現交叉,那麼久交換左右指標元素的位置,如果出現了交叉,就交換先開始掃瞄方向的指標與參考點的位置。

注意 兩個指標哪個先進行掃瞄? 答案是從參考點的對立方向開始掃瞄。

我們先來看看,如果從參考點同向掃瞄會出現什麼情況:選取陣列第乙個元素為參考點,此時left指標從lo+1開始掃瞄尋找比參考點大的元素,如果此時參考點的元素就是陣列最小的元素,那麼left指標立刻找到乙個比參考點大的元素並且停下來,然後right指標開始掃瞄,直到參考點的位置停下來,此時交換left指標(指向比參考點大的元素)和參考點(最小值)的位置,出現了參考點左側元素比參考點大的情況,不合題意。選擇對立方向開始掃瞄,不管左指標在與right指標碰撞時能否找到比參考點大的元素,right指標都已經找到了比參考點小的元素,在它們碰撞時(left=right)交換left與參考點的值,則不會出現違背題意的情況。

package com.night.baoly.sort;

public class twowayssort ;

quicksort(arr, 0, arr.length - 1);

for (int i : arr)

} private static void quicksort(int arr, int lo, int hi)

private static int getpartition(int arr, int lo, int hi)

while (left <= hi && arr[left] < pivot)

if (left < right) else

} return right;

} private static void swap(int arr, int a, int b)

}

6 三路快排

為什麼需要三路快排,二路快排已經能滿足需要了,可是由於它的分割方法,導致它在處理含有大量重複元素的陣列時,仍然將這些數放置在陣列的一端(大於等於參考數或小於等於參考數),由此一來,還是會產生高度不平衡的二叉樹。三路快排便應運而生了。

處理過程三路快排將陣列分割成三個部分:即 小於參考點的,等於參考點的,大與參考點的。處理思想和一路快排類似,只是對等於參考點的元素不做交換處理,提高了效率,具體過程請讀者根據一路快排的過程進行分析,下面給出**。

package com.night.baoly.sort;

public class threewayssort ;

quicksort(arr, 0, arr.length - 1);

for (int i : arr)

} private static void quicksort(int arr, int lo, int hi) else if (arr[current] > pivot) else

} swap(arr, lo, lt);

} private static void swap(int arr, int a, int b)

}

7 演算法優化

我們知道在處理小陣列時,插入排序比快速排序更快,那麼我們就可以在陣列規模較小時使用插入排序來代替快速排序,而不是無視陣列規模,在遞迴呼叫的過程中,全部呼叫快速排序演算法。

此外關於參考點資料的選擇,我們可以在陣列中隨機選擇乙個數,然後將它與陣列端點的元素交換位置,用作參考點。

深入理解快速排序

快速排序分為兩部分 2,快排函式本身,通過遞迴呼叫自身,每次呼叫可以使乙個元素回歸正確的位置,並使該元素之前的元素都小於該元素,之後的元素都大於該元素 遞迴的退出條件是base top 這樣,我們可以最後把整個數列割到最小兩個元素的子列,再使子列有序,從而整個數列都有序。值得注意的是,快排不會使某個...

快速排序理解

include include stdafx.h define n 7 void print2 int a printf n void sort int data,int left,int right int i left int j right int key data i while i j 左...

快速排序理解

每次處理好 某個 標桿 數字 在 最終 陣列 中的位置。然後 每個 數字 依次 作為標桿數,都找到 自己的 位置。相比 氣泡排序,冒泡 是每次找 最大數字的 位置,找好後 在 不包含最大 數字的 陣列中,再 次找 當前最大數的 位置。而快速 排序 是 找 指定 標桿數 的位置。標桿數 可以是任意乙個...