[英]QuickSort and Hoare Partition
我很難將QuickSort與Hoare分區轉換為C代碼,但無法找到原因。 我正在使用的代碼如下所示:
void QuickSort(int a[],int start,int end) {
int q=HoarePartition(a,start,end);
if (end<=start) return;
QuickSort(a,q+1,end);
QuickSort(a,start,q);
}
int HoarePartition (int a[],int p, int r) {
int x=a[p],i=p-1,j=r;
while (1) {
do j--; while (a[j] > x);
do i++; while (a[i] < x);
if (i < j)
swap(&a[i],&a[j]);
else
return j;
}
}
另外,我真的不明白為什么HoarePartition
有效。 有人可以解釋它為什么有效,或者至少把我鏈接到一篇文章嗎?
我已經看到了分區算法的逐步完成,但我沒有直觀的感覺。 在我的代碼中,它似乎甚至沒有用。 例如,給定數組
13 19 9 5 12 8 7 4 11 2 6 21
它將使用數據透視表13,但最終會使用數組
6 2 9 5 12 8 7 4 11 19 13 21
並且將返回j
,即a[j] = 11
。 我認為從那個點開始並且前進的數組應該具有比樞軸更大的值,這應該是真的,但是這不是真的,因為11 <13。
這是Hoare分區的偽代碼(來自CLRS,第二版),如果這很有用:
Hoare-Partition (A, p, r)
x ← A[p]
i ← p − 1
j ← r + 1
while TRUE
repeat j ← j − 1
until A[j] ≤ x
repeat i ← i + 1
until A[i] ≥ x
if i < j
exchange A[i] ↔ A[j]
else return j
謝謝!
編輯:
這個問題的正確C代碼將最終成為:
void QuickSort(int a[],int start,int end) {
int q;
if (end-start<2) return;
q=HoarePartition(a,start,end);
QuickSort(a,start,q);
QuickSort(a,q,end);
}
int HoarePartition (int a[],int p, int r) {
int x=a[p],i=p-1,j=r;
while (1) {
do j--; while (a[j] > x);
do i++; while (a[i] < x);
if (i < j)
swap(&a[i],&a[j]);
else
return j+1;
}
}
回答“為什么Hoare分區工作?”的問題:
讓我們將數組中的值簡化為三種: L值(小於透視值的值), E值(等於透視值)和G值(大於透視值的值)。
我們還將為數組中的一個位置指定一個特殊名稱; 我們將這個位置稱為s ,它是程序結束時j指針所在的位置。 我們是否事先知道哪個位置s是? 不,但我們知道某個位置會符合該描述。
使用這些術語,我們可以用稍微不同的術語表達分區過程的目標:它是將單個數組拆分成兩個較小的子數組,這些子數組不會相互錯誤排序 。 如果滿足以下條件,則滿足“未錯誤排序”的要求:
這才是我們真正需要做的。 我們甚至不用擔心E值在任何給定的傳球中都會結束。 只要每次傳遞使子陣列相對於彼此正確,后來的傳遞將處理任何子陣列內存在的任何障礙。
所以,現在讓我們來解決從對方的問題:如何划分程序保證有在S或以它的左邊沒有G值,並且不使用L值S的吧?
好吧,“ s右邊的值集合”與“ j指針到達s之前移動的單元格集”相同。 並且“包括s的左邊的值集合”與“在j到達s之前i指針移動的值的集合”相同。
這意味着在循環的某些迭代中,任何放錯位置的值都將位於我們的兩個指針中。 (為方便起見,我們說這是第j指針在L值指向,雖然它的工作原理完全為我指針在G值指向相同的。)在我的指針會在哪里,當第j指針是一個錯位的價值? 我們知道它將是:
請注意,有時i和j指針實際上都會停止在E值上。 發生這種情況時,即使不需要,也會切換值。 但這並沒有造成任何傷害; 我們之前說過, E值的放置不會導致子陣列之間的錯誤排序。
總而言之,Hoare分區的工作原理是:
我相信這段代碼存在兩個問題。 對於初學者來說,在你的Quicksort功能中,我想你想重新排序
int q=HoarePartition(a,start,end);
if (end<=start) return;
所以你有這樣的:
if (end<=start) return;
int q=HoarePartition(a,start,end);
但是,你應該做的比這更多; 特別是這應該讀
if (end - start < 2) return;
int q=HoarePartition(a,start,end);
原因是如果您嘗試分區的范圍大小為零或一,則Hoare分區無法正常工作。 在我的CLRS版本中,這里沒有提到; 我不得不去書的勘誤頁找到這個。 這幾乎可以肯定是“訪問超出范圍”錯誤所遇到的問題的原因,因為在不變的情況下,您可以直接從陣列運行!
至於Hoare分區的分析,我建議首先手動追蹤它。 還有一個更詳細的分析在這里 。 直觀地說,它通過從范圍的兩端向另一端增長兩個范圍來工作 - 一個在左側包含小於樞軸的元素,一個在右側包含比樞軸大的元素。 這可以稍微修改以產生Bentley-McIlroy分區算法(在鏈接中引用),該算法可以很好地擴展以處理相等的密鑰。
希望這可以幫助!
你的最終代碼是錯誤的,因為j
的初始值應該是r + 1
而不是r
。 否則,您的分區函數始終忽略最后一個值。
實際上,HoarePartition是有效的,因為對於包含至少2個元素(即p < r
)的任何數組A[p...r]
, A[p...j]
每個元素都是<=
A[j+1...r]
每個元素A[j+1...r]
終止時。 所以主算法重復出現的下兩個段是[start...q]
和[q+1...end]
所以正確的C代碼如下:
void QuickSort(int a[],int start,int end) {
if (end <= start) return;
int q=HoarePartition(a,start,end);
QuickSort(a,start,q);
QuickSort(a,q + 1,end);
}
int HoarePartition (int a[],int p, int r) {
int x=a[p],i=p-1,j=r+1;
while (1) {
do j--; while (a[j] > x);
do i++; while (a[i] < x);
if (i < j)
swap(&a[i],&a[j]);
else
return j;
}
}
更多說明:
分區部分只是偽代碼的翻譯。 (注意返回值是j
)
對於遞歸部分,請注意基本情況檢查( end <= start
而不是end <= start + 1
否則您將跳過[2 1]
子陣列)
你最后的C代碼是有效的。 但這並不直觀。 現在我幸運地正在學習CLRS。 在我看來,CLRS的偽代碼是錯誤的。(在2e)最后,我發現改變一個地方是正確的。
Hoare-Partition (A, p, r)
x ← A[p]
i ← p − 1
j ← r + 1
while TRUE
repeat j ← j − 1
until A[j] ≤ x
repeat i ← i + 1
until A[i] ≥ x
if i < j
exchange A[i] ↔ A[j]
else
exchnage A[r] ↔ A[i]
return i
是的,添加交換A [r]↔A [i]可以使其有效。 為什么? 因為A [i]現在大於A [r] OR i == r。 所以我們必須交換以保證分區的功能。
首先,你誤解了Hoare的分區算法,可以從c中的翻譯代碼中看出,因為你認為樞軸是子陣列的最左邊元素。
我會解釋你將最左邊的元素視為樞軸。
int HoarePartition (int a[],int p, int r)
這里p和r表示數組的下限和上限,它也可以是要分區的較大數組(子陣列)的一部分。
所以我們從最初指向數組終點之前和之后的指針(標記)開始(簡單地使用do while循環進行bcoz)。因此,
i=p-1,
j=r+1; //here u made mistake
現在按照分區我們希望樞軸左邊的每個元素都小於或等於pivot,大於pivot的右側。
因此,我們將移動'i'標記,直到我們得到大於或等於樞軸的元素。 類似'j'標記,直到我們發現元素小於或等於pivot。
現在,如果我<j我們交換元素bcoz這兩個元素都在數組的錯誤部分。 所以代碼將是
do j--; while (a[j] <= x); //look at inequality sign
do i++; while (a[i] >= x);
if (i < j)
swap(&a[i],&a[j]);
現在,如果'i'不小於'j',那意味着現在交換中沒有元素,所以我們返回'j'位置。
所以現在分區后半部分的數組是從'start to j'
上半部分是從'j + 1到結尾'
所以代碼看起來像
void QuickSort(int a[],int start,int end) {
int q=HoarePartition(a,start,end);
if (end<=start) return;
QuickSort(a,start,q);
QuickSort(a,q+1,end);
}
在Java中直接實現。
public class QuickSortWithHoarePartition {
public static void sort(int[] array) {
sortHelper(array, 0, array.length - 1);
}
private static void sortHelper(int[] array, int p, int r) {
if (p < r) {
int q = doHoarePartitioning(array, p, r);
sortHelper(array, p, q);
sortHelper(array, q + 1, r);
}
}
private static int doHoarePartitioning(int[] array, int p, int r) {
int pivot = array[p];
int i = p - 1;
int j = r + 1;
while (true) {
do {
i++;
}
while (array[i] < pivot);
do {
j--;
}
while (array[j] > pivot);
if (i < j) {
swap(array, i, j);
} else {
return j;
}
}
}
private static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.