理解快速排序和歸併排序
從本人很久以前的部落格上轉過來的。原創哈!
1,快速排序
快速排序通過分割值列,然後遞迴的對兩個部分進行排序,從而實現對值列的排序。它的基本思想是:通過一趟排序將要排序的資料分割成獨立的兩部分,其中一部分的所有資料都比另外一部分的所有資料都要小,然後再按此方法對這兩部分資料分別進行快速排序
首先任意選取一個數據(通常選用第一個資料)作為關鍵資料,然後將所有比它小的數都放到它前面,所有比它大的數都放到它後面,這個過程稱為一趟快速排序。
怎麼實現呢?---
它的關鍵在於完成一趟快排後,基準元素在哪個位置,每次都選取一個分割列的第一個元素作為基準元素,來尋找用它來分割排序列後它自己所處的位置,編寫一個
int findPartition(data,min,max)方法,用於使用data[min]作為基準元素把data[min]到data[max]分割為兩個部分,並返回分割以後基準元素自己所在的位置索引。
在主函式裡這樣來呼叫:
int mid;
if(min < max)
{
mid = findPartition(data,min,max);
quickSort(data,min,mid-1);
quickSort(data,mid
}
}
理解遞迴過程:::if(min < max),實際上就是要將值列分割為單一的元素(遞迴的最深一層),在得到基準元素的位置min後,基準元素在陣列中的位置就最終確定,剩下只對左右兩側的分割列
排序:quickSort(data,min,mid-1); quickSort(data,mid+1,max);
下面來看最重要的實現部分,如何實現findPartition函式:
將第一個元素作為基準元素不要動,設兩個指標left和right,left從左往右移動尋找比基準元素大的數,right從右往左移動尋找比基準元素小的數,當它們都找到對應的left和right的位置時,交換
這兩個元素。重複這個過程,直到right < left.這時,除基準元素外,從min+1到right的元素都比基準元素小,left到max的元素都比基準元素大。形成了這樣一個序列:
基準元素(data[min])
交換基準元素和data[right]即可得到 以基準元素為分割線的 左右兩個分割列。
這裡我把 排序遞迴的呼叫 與 分割值列和尋找分割位置(核心部分) 分開寫成兩個方法,為了顯得邏輯更加清楚。
完整程式碼 :
//快排:快排是一個遞迴的過程!!!!publicstaticvoid quickSort(Comparable[] data,int min,int max) {
int mid;
if(min < max)
{
mid = findPartition(data,min,max);
quickSort(data,min,mid-1);
quickSort(data,mid+1,max);
}
}
//快排的支援方法publicstaticint findPartition(Comparable[] data,int min,int max){
int left,right;
Comparable temp,partitionelement;
left = min;right = max;
partitionelement = data[min];//partitionelement 是data[min]的一個副本while(left < right)
{
while(data[left].compareTo(partitionelement) <=0&& left < right)
left++;
while(data[right].compareTo(partitionelement) >0)
right--;
if(left < right)
{
temp = data[left];
data[left] = data[right];
data[right] =temp;
}
}
temp = data[min];
data[min] = data[right];
data[right] = temp;
//錯錯誤的寫法,要的是把第一個元素和data[right]交換,而不是它的副本
//temp = data[right];
//data[right] = partitionelement;
//partitionelement = temp;return right;
}
2,歸併排序
歸併排序也是一個遞迴的實現,其關鍵在於合併兩個有序集:
void merge(Comparable[] data,int min,int mid,int max)
陣列data從min到mid有序,從mid+1到max有序,合併這兩部分,使得從min到max有序,且這個有序序列仍然放在data的從min到max下標裡。
這樣來遞迴呼叫:
int mid = (min + max)/2;
//遞迴的過程if(max > min){
mergSort(data,min,mid);
mergSort(data,mid+1,max);
merge(data,min,mid,max);
}
}
理解遞迴的過程:每次merg一次,在data原來的那一段位置上把data排好序了,不影響其他位置。
下面來實現關鍵部分,在data裡合併min到mid,mid+1到max兩個有序集,合併後位置仍放到min到max上,藉助於申請一個與data大小相等的空間!!!
這個地方開始沒有想清楚:應該是Comparable[] temp = new Comparable[max+1];
而不是Comparable[] temp = new Comparable[max - min + 1];想一想為什麼?
每遞迴一次就會呼叫一次merge,實際上temp每次都會重新分配一下,利用temp把data從min到max排好序,並放在data的這一段位置上
完整程式碼:
//歸併排序:歸併是一個遞迴的過程,其關鍵部分在於合併2個有序集publicstaticvoid mergSort(Comparable[] data,int min,int max){
int mid = (min + max)/2;
//遞迴的過程if(max > min){
mergSort(data,min,mid);
mergSort(data,mid+1,max);
merge(data,min,mid,max);
}
}
//支援方法:合併2個有序集,藉助於申請一個與兩個有序集大小之和相等的空間。publicstaticvoid merge(Comparable[] data,int min,int mid,int max){
Comparable[] temp =new Comparable[max+1];
//Comparable[] temp = new Comparable[max - min + 1];錯誤!!!int begin1 = min;
int begin2 = mid +1;
int index = min;//注意index是從min開始的!!!while(begin1 <= mid && begin2 <= max){
if(data[begin1].compareTo(data[begin2]) <0)
temp[index++] = data[begin1++];
else
temp[index++] = data[begin2++];
}
if(begin1 > mid)
for(int i = begin2;i <= max;i++)
temp[index++] = data[i];
if(begin2 > max)
for(int i = begin1;i <= mid;i++)
temp[index++] = data[i];
for(int i = min;i <= max;i++)//對應的位置!!!! data[i] = temp[i];
}
}
這個程式除錯了半天才好,主要還是沒把遞迴的過程想清楚,data在每次遞迴的過程中不能被覆蓋,它排好了序的部分就不能動了!!!
另外就是temp申請空間這個地方,是max+1,而不是max-min+1的大小,否則會導致陣列越界
剛才程式碼效果亂了,悲劇 ,又重新複製了一遍,耽誤了半天時間。
好了,有空再寫個效能的比較總結。