演算法鋪子之排序 快速排序(一)

2021-08-04 09:50:28 字數 2620 閱讀 1023

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

以公升序為例。首先會找乙個元素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的最終結果了。

從圖可以看出在資料的有序程度不高的情況下,同是o(nlogn)的歸併排序演算法的速度沒有快速排序高。

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

歸併排序之所以是o(nlogn)的排序演算法,是因為會將待排序列一分為二,然後再一分為二,以此類推。那麼整個層數就是logn的,合併的時候是n的,所以是o(nlogn)的演算法。

而快速排序演算法也是一分為二,逐步劃分的過程,但快速排序二分出來的子串行可能是一大一小,而不是正好的一半一半。

如果在最壞的情況下(完全有序的情況),快速排序二分時的二叉樹也就退化成了鍊錶,也就是說退化成了o(n^2)的演算法。

為了解決這個退化問題,可以在選定基準的時候進行隨機選擇,從概率上來講,對近乎有序的資料使用快速排序時隨機選擇基準,而不是固定選擇最左邊的元素為基準的話,退化成o(n^2)的概率幾乎為0。(退化的概率=1/n*1/(n-1)*1/(n-2)…*1)

那麼優化的**如下:

template

int parttion(vector

& data, int left, int right)

}swap(data[j], data[l]);

return j;

}

經過優化之後,實驗結果如下:

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

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

演算法鋪子之排序 堆排序(一)

堆排序 heapsort 是指利用堆積樹 堆 這種資料結構所設計的一種排序演算法,它是選擇排序的一種。可以利用陣列的特點快速定位指定索引的元素。堆分為大根堆和小根堆,是完全二叉樹。大根堆的要求是每個節點的值都不大於其父節點的值,即a parent i a i 在陣列的非降序排序中,需要使用的就是大根...

演算法鋪子之排序 插入排序

插入排序和選擇排序同樣是一種較為常見且容易實現的o n 2 級別的排序演算法,但插入排序的效率和待排序資料的有序程度有關係,待排序資料的有序程度越高,插入排序的效率也就越高,甚至有時候能比o nlogn 級別的排序演算法的效率還要高。所以插入排序也通常會成為其他排序演算法的子過程。插入排序的思路和我...

排序演算法之快速排序

快速排序使用分治法 divide and conquer 策略來把乙個序列 list 分為兩個子串行 sub lists 步驟為 從數列中挑出乙個元素,稱為 基準 pivot 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面 相同的數可以到任一邊 在這個分割槽退出...