快排擴充套件,Partition函式的應用 一

2021-10-02 21:17:29 字數 3272 閱讀 8266

擴充套件分析

總結快排作為平均速度最快的一種內部排序,基礎思想是基於分治的。關於分治演算法設計思想總結,我在講解演算法設計思路這篇文章講到過。快速排序中乙個非常核心的地方就是partition函式的作用,這個函式支撐了快排的分的過程。我覺得這個函式重要,不單是因為其支撐了快排,而且在很多演算法設計的過程中也都可以使用的上。

關於這個擴充套件,我打算分兩次文章來寫,我會著重的講到問題的轉換,這算是乙個非常重要的演算法求解能力。以下仍然從題目開始。

輸入乙個整數陣列,實現乙個函式來調整該陣列中數字的順序,使得所有的奇數字於陣列的前半部分,所有的偶數字於陣列的後半部分。

簡單粗暴的方式,可能就是類似於排序的方式,把偶數移動到後面,這種暴力法太過浪費了,這麼高的複雜度排個序都夠了,但是題目中顯然沒有要求排序,這樣就太不合適,我們思考其他更低複雜度的演算法。

乍一看這個題目好像和partition函式的關係不大,但是仔細分析就能夠發現聯絡。 partition函式是根據與pivot的大小關係,把資料分為兩部分,而本題呢,是根據資料的奇偶性把資料分為兩部分。再看題目中奇數字於陣列的前半部分,奇數字於陣列的後半部分,竟然與partition函式中比pivot小的放在前半部分,大的放在後半部分不謀而和。

python**

partition函式是判斷大小進行前移或者後移,這裡就改變判斷條件,根據奇偶性前移或者後移即可。因為快排要多次呼叫partition函式,故而將其作為子函式呼叫,而這裡只需要一趟移動即可,直接乙個函式就夠了。

def

partition

(array)

: start, end=0,

len(array)-1

while startwhile startnot array[end]&1

: end-=

1 array[start]

, array[end]

= array[end]

, array[start]

while start: start+=

1 array[start]

,array[end]

=array[end]

,array[start]

return array

我們提到過根據××把資料分為兩部分,我們上面已經提到了兩種情況,同時可能還有其他情況,我們可以總結昇華一下,對於所有的判斷條件,無非結果都是得到乙個布林值,真或假。那我們可以把所有的情況都抽象出來,而把判斷條件作為乙個函式,這個函式就是單獨的返回布林值。

這樣的分析就能使我們的**進行解耦,這樣就能處理任何情況下對陣列的劃分。

python**

def

isodd

(number)

:# 這裡返回的int型別可以轉換為bool型別

return number&

1def

partition

(array,condition)

:# condition引數是乙個函式

start, end=0,

len(array)-1

while startwhile startnot condition(array[end]):

end-=

1 array[start]

, array[end]

= array[end]

, array[start]

while startstart+=

1 array[start]

,array[end]

=array[end]

,array[start]

return array

如果題目要求,使得所有的奇數字於陣列的前半部分,所有的偶數字於陣列的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變,那上面的方式可就不行了,因為我們都知道 快排是乙個不穩定的排序,主要原因就是因為partition函式這裡會改變相等元素的相對位置,因為大小判斷變味了奇偶判斷,那麼值相等就對應了奇偶性相等 。所以partition函式也就會改變同奇偶性資料之間的相對位置。

題目一變這種方法就失效了,需要尋求其他方法。

犧牲空間換時間,時間o(n),空間o(n)的演算法

在演算法設計中,常會遇到這種抉擇的過程,而 伴隨計算機發展的乙個重要思想就是權衡(tradeoff)。不能在兩個維度上都得到最好的解的時候,就需要權衡。所以我們不得已犧牲空間換時間,要想快速解決這個問題,就得付出空間的代價。

這種思路也很簡單,就直接重新開闢一塊空間,然後對陣列掃瞄兩次,第一次把奇數複製出去,第二次把偶數複製出去,放在奇數的後面。掃瞄兩次,時間複雜度是o(n),整個陣列複製了一次空間複雜度是o(n)。其他優化不能做到複雜度級別的降低,不予講解。

這種方法的**很好寫,就不過多強調。但是這種方法的前提是可以使用額外空間。如果不能使用,我們就得另謀他法。

基於排序的方法

我們之前說過既然可以借鑑排序的思想,那我們就借鑑到底,我們把奇數全都當做0,偶數全都當做1,這樣移動的結果就相當於進行了一趟排序,題目要求不改變元素的相對位置,這就意味著要求排序是穩定的。所以不穩定的排序直接排除(快排、堆排序、選擇排序、希爾排序)。現在剩下穩定的排序(歸併排序、氣泡排序、插入排序、計數排序),以及建立在其他排序基礎上的穩定排序(桶排序、基數排序)。建立在其他排序基礎之上的直接排序了,因為問題簡單,用這兩個排序降低不了複雜度。

基本上可以確定的剩下這幾個穩定的基礎排序,排序本身的複雜度就是解決這個問題的複雜度。

排序時間複雜度

空間複雜度

描述計數排序

o (n

)o(n)

o(n)

o (n

)o(n)

o(n)

需要占用額外空間

歸併排序

o (n

lgn)

o(nlgn)

o(nlgn)o(

n)

o(n)

o(n)

各種性質都比不上計數排序

插入排序

o (n

2)

o(n^2)

o(n2)o(1

)o(1)

o(1)

慢但是不占用額外空間,只有偶數才需要後移

氣泡排序

o (n

2)

o(n^2)

o(n2)o(1

)o(1)

o(1)

時間複雜度高但是不占用額外空間

這裡的思想很簡單明瞭了,**我就略去,可見各個解法都有缺陷。

快排的兩種partition函式

partition函式就是快排的關鍵部分,作用是將陣列劃分成兩部分,左邊小於基數,右邊大於基數 但實際上它也不僅僅用於快排,在求top k 問題中也常常會用到。下面介紹兩種partition函式,他們都是雙指標的方法,但具體會有差異 函式一 頭尾指標向中間夾 def partition array,...

快排光芒下被忽視的Partition函式

看到這篇標題,沒有學過快排的人自然是不知道partition函式的意思和作用,這裡附上學習的連線 lantian的快排總結 我們現在都是被快排蒙蔽了雙眼,沒有意識到快最核心的劃分函式partition,當然partition函式也就不止於快排這裡,本文就從多方面來為展示partition函式的本質和...

快排partition過程 索引小修改

原偽 1partition a,p,r 2x a r 3i p 1 4for j p r 1 5if a j x 6i 7exchange a i a j 8exchange a i 1 a r 9return i 1 索引i指向 主元x的前子陣列的最後乙個元素 包括 劍指offer 中也是這樣實現...