1. 程式人生 > >演算法介紹(2) 快速排序演算法

演算法介紹(2) 快速排序演算法

                  本篇介紹快速排序演算法,以及相應的時間空間複雜度求解。        
         首先介紹一下氣泡排序的時間複雜度: 對於n位的數列則有比較次數為 (n-1) + (n-2) + ... + 1 = n * (n - 1) / 2,這就得到了最大的比較次數而O(N^2)表示的是複雜度的數量級。舉個例子來說,如果n = 10000,那麼 n(n-1)/2 = (n^2 - n) / 2 = (100000000 - 10000) / 2,相對10^8來說,10000小的可以忽略不計了,所以總計算次數約為0.5 * N^2。用O(N^2)就表示了其數量級(忽略前面係數0.5)。在沒有改進的情況下,氣泡排序的時間複雜度都是相同的,為O(N^2)。以下改進的方法可以使最佳情況時為O(n)。
         
public void bubbleSort(int arr[]) {
    boolean didSwap;
    for(int i = 0, len = arr.length; i < len - 1; i++) {
        didSwap = false;
        for(int j = 0; j < len - i - 1; j++) {
            if(arr[j + 1] < arr[j]) {
                swap(arr, j, j + 1);
                didSwap = true;
            }
        }
        if(didSwap == false)
            return;
    }    
}
          快速排序的基本思想如下:首先在要排序的序列 a 中選取一箇中軸值,而後將序列分成兩個部分,其中左邊的部分 b 中的元素均小於或者等於 中軸值,右邊的部分 c 的元素 均大於或者等於中軸值,而後通過遞迴呼叫快速排序的過程分別對兩個部分進行排序,最後將兩部分產生的結果合併即可得到最後的排序序列。   為了實現一次劃分,我們可以從陣列(假定資料是存在陣列中)的兩端移動下標,必要時交換記錄,直到陣列兩端的下標相遇為止。為此,我們附設兩個指標(下角標)i 和 j, 通過 j 從當前序列的有段向左掃描,越過不小於基準值的記錄。當遇到小於基準值的記錄時,掃描停止。通過 i 從當前序列的左端向右掃描,越過小於基準值的記錄。當遇到不小於基準值的記錄時,掃描停止。交換兩個方向掃描停止的記錄 a[j] 與 a[i]。 然後,繼續掃描,直至 i 與 j 相遇為止。掃描和交換的過程結束。這是 i 左邊的記錄的關鍵字值都小於基準值,右邊的記錄的關鍵字值都不小於基準值。   
          快速排序的時間複雜度為:
T(n) <= cn + 2T(n/2)    c是一個常數
     <= cn + 2(cn/2+2T(n/4)) = 2cn+ 4T(n/4)

     <= 2cn + 4(cn/4+ 2T(n/8)) = 3cn + 8T(n/8)
      …… ……

     <= cnlogn + nT(1) = O(nlogn)      其中cn 是一次劃分所用的時間,c是一個常數

  最壞的情況,每次劃分都得到一個子序列,時間複雜度為:

  T(n) = cn + T(n-1)

     = cn + c(n-1) + T(n - 2) = 2cn -c + T(n-2)

            = 2cn -c + c(n - 2) + T(n-3) = 3cn -3c + T(n-3)
    ……

    = c[n(n+1)/2-1] + T(1) =  O(n2)

  快速排序的時間複雜度在平均情況下介於最佳與最差情況之間,假設每一次分割時,基準值處於最終排序好的位置的概率是一樣的,基準值將陣列分成長度為0 和 n-1,1 和 n-2,……的概率都是 1/n。在這種假設下,快速排序的平均時間複雜性為:
    T(n) = cn + 1/n(T(k)+ T(n-k-1))   T(0) = c, T(1) = c

  這是一個遞推公式,T(k)和T(n-k-1)是指處理長度為 k 和 n-k-1 陣列是快速排序演算法所花費的時間, 根據公式所推算出來的時間為 O(nlogn)。因此快速排序的平均時間複雜性為O(nlogn)。

        空間複雜度為:

        快速排序需要棧空間來實現遞迴,如果陣列按局等方式被分割時,則最大的遞迴深度為 log n,需要的棧空間為 O(log n)。最壞的情況下在遞迴的每一級上,陣列分割成長度為0的左子陣列和長度為 n - 1 的右陣列。這種情況下,遞迴的深度就成為 n,需要的棧空間為 O(n)。
        以下貼出快速排序的程式碼:
 
       
#include <iostream>
using namespace std;

int a[101], n;

void quickSort(int left, int right)
{
	int i, j, t, temp;
	if (left > right)
		return;




	temp = a[left];
	i = left;
	j = right;
	while (i != j)
	{
		while (a[j] >= temp && i < j)
			j--;
		while (a[i] <= temp && i < j)
			i++;
		if (i < j)
		{
			t = a[i];
			a[i] = a[j];
			a[j] = t;
		}
	}
	a[left] = a[i];
	a[i] = temp;

	quickSort(left, i - 1);
	quickSort(i + 1, right);
}

int main()
{
	int i;
	cin >> n;
	for (i = 0; i < n; i++)
	{
		cin >> a[i];
	}

	quickSort(0, n - 1);

	for (i = 0; i < n; i++)
	{
		cout << a[i] << " ";
	}

	system("pause");
	return 0;
}