快速排序中的分割演算法的解析與應用

2022-03-18 02:26:50 字數 4588 閱讀 7041

一,分割(partition)演算法介紹

所謂分割演算法,先選定乙個樞軸元素,然後 將陣列中的元素分成兩部分:比樞軸元素小的部分都位於樞軸元素左邊;比樞軸元素大的部分都位於樞軸元素右邊

此時,樞軸元素在陣列中的位置就被「永久地確定」下來了---將整個陣列排序,該樞軸元素的位置不會變化。

另外,樞軸元素的選取對分割演算法至關重要。一般而言,終極追求的是:將陣列平分。因此,盡可能地讓樞軸元素的選取隨機化和靠近中位數。

這裡採用「三數取中」法選取樞軸元素。

關於快速排序排序演算法,可參考:排序演算法總結之快速排序

二,分割演算法的實現

1

//分割陣列,將陣列分成兩部分. 一部分比pivot(樞軸元素)大,另一部分比pivot小

2private

static

int parition(int arr, int left, int

right)

11while(arr[--j] >pivot){}

12if(i 13swap(arr, i, j);

14else

15break;16

}1718 swap(arr, i, right-1);//

restore pivot, 將樞軸元素放置到合適位置:arr左邊元素都比pivot小,右邊都比pivot大

19return i;//

返回 pivot的 索引

20 }

①第4行,樞軸元素是通過「三數取中」法選擇的。在「三數取中」時,還做了一些優化:將 樞軸元素 放到 陣列末尾的倒數第二個位置處。具體參考 media3()

需要注意的是:當輸入的陣列中長度為1 或者 2 時, partition會出現向下越界(但對快排而言,當陣列長度很小的,其實可以不用 partition,而是直接用插入排序)。因此,可加入以下的修改。

1

//分割陣列,將陣列分成兩部分. 一部分比pivot(樞軸元素)大,另一部分比pivot小

2private

static

int parition(int arr, int left, int

right)

15while(arr[--j] >pivot){}

16if(i 17swap(arr, i, j);

18else

19break;20

}2122 swap(arr, i, right-1);//

restore pivot 將樞軸元素放置到合適位置:arr左邊元素都比pivot小,右邊都比pivot大

23return i;//

返回 pivot的 索引

24 }

再來看看,三數取中演算法,這裡也有個特殊情況:當陣列中元素個數都沒有3個時....怎麼辦?

1

//三數取中,用在快排中隨機選擇樞軸元素時

2private

static

int media3(int arr, int left, int

right)

其實,這裡的「三數取中」的實現,與參考資料中提到的三數取中實現有一點不同。這是正常的,畢竟實現細節不同。如果有錯誤,需要自行除錯。

這裡提下第3-7行的兩個if語句:當需要 「取中」的目標陣列長度為1時,或者說 對陣列中某些範圍內[left, right]的元素進行「取中」時,若left=right,則根本就沒有3個數,違背了「三數取中」的本意(隨機地選取樞軸元素),故直接 return。

當陣列中元素只有乙個時,第18行會越界。為了防止這種情況,在第3-4行就先對陣列長度進行判斷。當陣列中只有兩個元素,其實就相當於 center=left,因此,程式也沒問題。

三,分割演算法的應用---o(n)時間複雜度找出無序陣列中第k大的元素

給定乙個陣列,陣列中某個元素出現的次數超過了陣列大小的一半,找出這個元素。

比如輸入:[2,5,4,4,5,5,5,6,5] ,輸出 5

這個問題,其實可以轉化成求解中位數問題。因為,當陣列有序時,出現次數超過一半的那個元素一定位於陣列的中間。

所謂中位數,就是 假設 陣列是有序的情況下,中間那個元素。即 arr[arr.length/2]

而要求解中位數,當然可以先對陣列進行排序,但排序的時間複雜度為o(nlogn),那有沒有更快的演算法?

當然是有的。就是借助partition分割演算法 來 實現。

1

//找出 arr 中 第 n/2 大的那個元素

2public

static

int media_number(int

arr)

15else19}

20return

arr[center];

21 }

上面演算法不僅可以求解「找出超過一半的數字」,也可以求解任何乙個陣列的中位數。

這裡遞迴表示式 t(n)=t(n/2)+o(n),o(n)表示將陣列 分成兩部分所花的代價。

故時間複雜度為o(n)

四,參考資料

整個完整**

public

class

middle_large

else

}return

arr[center];

}//分割陣列,將陣列分成兩部分. 一部分比pivot(樞軸元素)大,另一部分比pivot小

private

static

int parition(int arr, int left, int

right)

while(arr[--j] >pivot){}

if(i swap(arr, i, j);

else

break

; }

swap(arr, i, right-1);//

restore pivot 將樞軸元素放置到合適位置:arr左邊元素都比pivot小,右邊都比pivot大

return i;//

返回 pivot的 索引

}

//三數取中,用在快排中隨機選擇樞軸元素時

private

static

int media3(int arr, int left, int

right)

private

static

void swap(int arr, int left, int

right)

public

static

void

main(string args) ;

int result =media_number(arr);

system.out.println(result);

}}

另外,再寫了乙個尋找第k(k從1開始)大元素的程式:

public

class

findklargest

if (k < 0)

if (k > arr.length - 1)

int low = 0;

int high = arr.length - 1;

int pivot_index =partition(arr, low, high);

while (pivot_index !=k)

else

}return

arr[pivot_index];

}public

static

extends comparable<? super t>> int partition(t arr, int low, int

high)

while (arr[j--].compareto(pivot) == 1)

if (i else

}return i - 1;

}private

static

extends comparable<? super t>> t pick(t arr, int low, int

high)

private

static

extends comparable<? super t>> void swap(t arr, int i, int

j)

public

static

void

main(string args) ;

system.out.println(findk(strings, 1));

0));

string strings1 = ;

system.out.println(findk(strings1, 2));

long longs = ;

system.out.println(findk(longs, 5));

system.out.println(findk(longs, 1));

system.out.println(findk(longs, 2));

}}

原文:

快速排序中的分割演算法的解析與應用

一,分割 partition 演算法介紹 所謂分割演算法,先選定乙個樞軸元素,然後 將陣列中的元素分成兩部分 比樞軸元素小的部分都位於樞軸元素左邊 比樞軸元素大的部分都位於樞軸元素右邊 此時,樞軸元素在陣列中的位置就被 永久地確定 下來了 將整個陣列排序,該樞軸元素的位置不會變化。另外,樞軸元素的選...

Hadoop中的快速排序演算法

在hadoop中,排序是mapreduce框架中最重要的操作之一,map task和reduce task都會對資料按照key排序,不管邏輯上是否真的需要排序,任何程式中的資料都會被排序,這是hadoop的預設行為。b mapreduce中使用了兩種排序演算法 快速排序和優先佇列。在map和redu...

談談演算法中的快速排序

快速排序 快速排序是c.r.a.hoare於1962年提出的一種劃分交換排序。它採用了一種分治的策略,通常稱其為分治法 divide and conquermethod 快速排序使用分治法把乙個陣列,分為兩個子陣列,該方法的基本思想是 1 先從數列中取出乙個數作為基準數。2 分割槽過程,將比這個數大...