內部排序(二)快速排序
快速排序(QuickSort)是對氣泡排序的一種改進,故在介紹快速排序之前,我們先介紹氣泡排序。
1、氣泡排序
原理:將第一個記錄的關鍵字和第二個記錄的關鍵字進行比較,若為逆序,則交換(假設正序輸出),然後比較第二個記錄和第三個記錄的關鍵字。以此類推,直至第n-1個記錄和第n個記錄的關鍵字進行比較,這是第一趟。然後第二趟,第三趟……
過程演示:
演算法程式碼:(演算法很簡單,雙重迴圈)
#include <stdio.h>
#include <stdlib.h>
void main(){
int array[5] = { 5, 3, 1, 2 , 8 };
int temp=array[0];//臨時變數
int length=5;
for (int i = 0; i < length-1; i++){//外迴圈控制趟數
for (int j = 0; j < length-1-i; j++){//內迴圈控制比較
if (array[j]>array[j+1]){
temp = array[j];
array[j] = array[j+1];
array[j + 1] = temp;
}
}
}
for (int k = 0; k < length; k++){
printf("%d", array[k]);
}
system("pause");
}
2、快速排序
原理:快排是對氣泡排序的一種改進。基本思想是通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,已達到整個序列有序。
演算法敘述:一趟排序的具體做法是:附設兩個指標low和high,它們的初值分別是low和high,設樞軸記錄的關鍵字為pivotkey,則首先從high所指位置向前搜尋找到第一個關鍵字小於pivotkey的記錄,和樞軸記錄交換之(即逆序交換,假設我們要從小到大輸出),然後從low所指位置起向後搜尋,找到第一個關鍵字大於pivotkey的記錄,和樞軸相互交換(逆序交換)重複這兩部,直至low=high為止。
此時記錄被樞軸分成兩部分,左邊的都比樞軸小,右邊的都比樞軸大。
然後進行第二趟,將樞軸左邊的全部記錄再當成一個記錄序列,再次使用剛剛的方法。
演算法演示
(i就是low,j就是high)
演算法程式碼:
#define MAXSIZE 20
typedef int KeyType;//定義關鍵字型別為int
typedef char InfoType;//定義其他資訊型別為char
typedef struct{//定義記錄型別
KeyType key;
InfoType otherinfo;
}RedType;
typedef struct{//定義順序表型別
RedType r[MAXSIZE + 1];//r[0]閒置或作為哨兵
int length;//順序表的長度
}SqList;
//在實現我們剛剛的演算法時,每交換一次,需進行三次移動。
//但實際上對樞軸記錄的移動是多餘的,
//因為只有在一趟排序結束時,即low=high的位置才是樞軸記錄的最後位置。
//故我們將樞軸暫存到r[0],一趟結束後再移到正確位置。
//此函式進行一趟排序。返回樞軸的位置。(此過程的演示只需把上圖每一次交換的樞軸量用空白代替就行)
int partition(SqList *L, int low, int high){
int pivotkey;
L->r[0] = L->r[low];//用子表的第一個記錄作為樞軸記錄。
pivotkey = L->r[low].key;//用pivotpoint標識
while (low < high){
while (low<high&&L->r[high].key>=pivotkey){//從後向前找到要(向前)移動的關鍵字的下標
high--;
}
L->r[low] = L->r[high];//把找的的點(向前)移動
while (low < high&&L->r[low].key <= pivotkey){//從前往後找到要(向後)移動的關鍵字的下標
low++;
}
L->r[high] = L->r[low];//把找到的點(向後)移動
}
L->r[low] = L->r[0];//樞軸量放到該放置的位置
return low;//返回樞軸量的下標
}
void QSort(SqList *L, int low, int high){
//對順序表中子序列L->r[low...high]作快速排序
int pivotloc;
if (low<high){//如果長度大於1
pivotloc = partition(L, low, high);//進行一趟排序,將L->r[low...high]分為兩個子序列,本趟樞軸下標賦給pivotloc
QSort(L, low, pivotloc - 1);//對低子表進行遞迴排序
QSort(L, pivotloc + 1, high);//對高子表進行遞迴排序
}
}
void QuickSort(SqList *L){
QSort(L,1,L.length);
}
評價:
優點:快速排序的平均時間為T(n)=knInn,其中n為待排序列中記錄的個數,k為某個時間常數,經驗證明,在所有同數量級的此類排序方法中,快速排序的常數因子k最小。因此,就平均時間而言,快速排序是目前被認為最好的一種內部排序方法。
缺點:若初始記錄序列按關鍵字有序或基本有序時,快速排序將退化為氣泡排序,時間複雜度為O(n^2).改進的方法是依“三者取中”的法則來選樞軸記錄。(此處不詳細介紹)