1、算法思想
選定一個元素作爲支點(pivot),以支點爲基準將整個序列劃分爲兩個子序列r1 … ri-1和ri+1 … rn,前一個子序列中記錄的值均小於或等於支點,後一個子序列中元素的值均大於或等於支點,這個過程稱作一趟快速排序(如下圖所示)。然後只要分別對這兩部分繼續進行遞歸地排序,就可以使整個序列有序。
一趟快速排序的具體做法:
1)、設置兩個指針low和high,剛開始分別指向序列的第一個、最後一個的元素。初始化支點(pivot)也爲序列的第一個元素。(這是爲了方便編程。這裏有種錯覺:好像pivot只能爲第一個元素。其實是可以取序列中的任意一個元素的,只要將選定的元素和第一個元素交換一下就可以了。)
2)、首先,從high所指定位置起向前搜索找到第一個小於pivot的元素和low所指向的元素(即pivot)互相交換,然後從low所指位置起向後搜索,找到第一個大於pivot的元素和high所指向的元素(即pivot)交換。
3)、重複2)中的兩步,直到low=high
2、基本算法實現
int partition(int data[], int low, int high) //返回pivot所在的位置
{
int pivot = data[low];
while (low < high)
{
while (low < high && data[high] >= pivot) high--;
swap(data[low], data[high]); //high指向的值比支點值小,換到前面去。和low指向的值交換也就是和pivot交換
while (low < high && data[low] <= pivot) low++;
swap(data[low], data[high]); //low指向的值比支點值大,換到後面去。此時,high指向的值等於pivot
//和high交換也就是和pivot交換
}
return low; //此時low == high。可以發現:pivot其實就是不停地在被“丟來丟去”,不是在high處就是在low處。
//所以這裏返回high也可以。這也解釋了爲什麼low == high時,算法就停止了,因爲low和high指向的value已經是pivot了。
}
void QSort(int data[], int low, int high) //完整快速排序
{
if (low < high)
{
int pivotloc = partition(data, low, high);
QSort(data, low, pivotloc - 1); //遞歸
QSort(data, pivotloc + 1, high); //遞歸
}
}
int main(int argc, char *argv[])
{
int data[20] = { 1, 7, 3, 50, 43, 34, 78, 23, 67, 90 };
QSort(data, 0, 9); //調用快排
int i = 0;
while (i < 10) //輸出
{
cout << data[i] << ' ';
++i;
}
system("pause");
return 0;
}
3、改進快排算法
上面算法中,每交換一對記錄需要進行3次記錄移動操作(即swap函數)。從代碼和註釋中可以看出,其實pivot一直在被“丟來丟去”,所以沒有必要一直移動pivot,也就是可以將pivot暫存起來,然後將pivot賦值到最終的位置(一共就修改了4行代碼:第5、9、12、14行)。修改版代碼如下:
//微改進快排
int partition(int data[], int low, int high) //返回pivot所在的位置
{
int pivot = data[low];
data[0] = pivot; //暫存
while (low < high)
{
while (low < high && data[high] >= pivot) high--;
data[low] = data[high]; //第一次執行時,覆蓋了pivot,因爲pivot已經暫存到data[0]處
while (low < high && data[low] <= pivot) low++;
data[high] = data[low]; //由於上一步的執行,data[high]已經複製到了low處
}
data[low] = data[0];
return low;
}
void QSort(int data[], int low, int high) //完整快速排序
{
if (low < high)
{
int pivotloc = partition(data, low, high);
QSort(data, low, pivotloc - 1); //遞歸
QSort(data, pivotloc + 1, high); //遞歸
}
}
int main(int argc, char *argv[])
{
int data[20] = { 0, 1, 7, 3, 50, 43, 34, 78, 23, 67, 90 }; //第0個位置用於暫存pivot
QSort(data, 1, 10); //調用快排,對第1—10個位置上的值進行排序
int i = 1;
while (i <= 10) //輸出
{
cout << data[i] << ' ';
++i;
}
system("pause");
return 0;
}
下面給出一個一趟快排的例子,也就是partition函數的執行過程。其中i表示low,j表示high,最右邊的49表示暫存的pivot,用於進行比較:
參考文獻:
《數據結構》,嚴蔚敏
《算法設計與分析》,王紅梅