快速排序是一種性能較好的算法,有最好的平均性能。由冒泡排序算法改進,採用分治思想:
1.將整個區間劃分兩個子區間p1, p2和元素k, 使得p1區間的所有元素均小於k, p2區間的所有元素均大於或等於k(這裏k成爲樞軸元素),
2.再對p1和p2兩個區間分別重複步驟1, 如此遞歸
3.由於步驟1是就地排序,直到最後每個區間的元素個數爲1, 這樣整個區間就有序了。
實現步驟1的函數
int Partion(int *p, int low, int high)
這個函數根據選擇的樞軸元素不同, 實現也不同,常見的有:
1.第一個元素選爲樞軸元素int Partion(int *p, int low, int high)
{
while(high>low)
{
while(high>low && p[high]>p[low]) //#1
{
--high;
}
swap(p+high, p+low);
for(high>low && p[high]>=p[low]) //注意這裏和#1處必須保證至少有一個地方取等號, 否則遇到相等的元素時會出現死循環, 函數不能退出。
{
++low;
}
swap(p+high, p+low);
}
return low;
}
2.最後一個元素選爲樞軸元素int Partion(int *p, int low, int high)
{
int i, j = low;
for(i=low; i<high-1; ++i)
{
if(p[i]<=p[high])
{
if(i!=j) // 避免不必要的交換
{
swap(p+i, p+j);
}
++j;
}
}
swap(p+j, p+high);
return j;
}
當序列有序的時候, 不管是選第1個還是最後一個元素爲樞軸元素, 都會劃分一個長度爲1, 一個長度爲N-2的兩個子區間,子區間長度相差較大,所有子區間組成的樹不平衡,造成排序時間複雜度爲O(n^2),效率較低。可以採用隨機化的方法選擇樞軸元素:
int partion(int *p, int low, int high)
{
int k = random() ;
swap(p+k, p+low); // 或者swap(p+k, p+high);
// 隨機選擇一個元素與第一個元素或者最後一個元素交換, 作爲樞軸元素
//
// 接下來與第一個或者最後一個元素作爲樞軸元素相同
....
}
這樣效率可以達到O(n*logn)。
通常快速排序具有最好的平均性能,時間複雜度爲O(n*logn), 常數因子較其他同類算法(時間複雜度爲O(n*logn)的算法)小。可是當區間元素有序(正序或者逆序)的時候,快速排序就蛻變成時間複雜度爲O(n^2)的算法了。
代碼實現
1.遞歸實現
void static qsort(int *p, int low, int high)
{
if(low < high)
{
int pivot = partion(p, low, high);
qsort(p, low, pivot-1);
qsort(p, pivot+1, high);
}
}
2. 非遞歸實現
void qsort(int *p, int low, int high)
if(low < high)
{
Stack stack;
init_stack(&stack);
int mid = partion(p, low, high);
if(mid-1 >= 0)
{
push_stack(&stack, low);
push_stack(&stack, mid-1);
}
if(mid+1 <= high)
{
push_stack(&stack, mid+1);
push_stack(&stack, high);
}
while(!empty_statck(&stack))
{
int m, n;
if(pop_stack(&stack, &n) && pop_stack(&stack, &m))
{
mid = partion(p, m, n);
if(mid-1 >= m)
{
push_stack(&stack, m);
push_stack(&stack, mid-1);
}
if(mid+1 <= n)
{
push_stack(&stack, mid+1);
push_stack(&stack, n);
}
}
}
}
}