內部排序算法(交換排序,插入排序)注意點(C語言實現)


 

  對於算法思想的理解可以參考下面的這個帖子,十大經典排序算法(動圖演示) - 一像素 - 博客園,因為算法的邏輯和數學很像,相應的基礎資料一般也能在網上找到,所以,本帖子這談論一些重要的注意點,其他人講到的我就不提了,在實現的過程中可能有些代碼不是很理解,其他的就相對比較容易多了。

整體按照這個順序來,也比較好記憶一點:

 

一、交換排序

1、冒泡排序,基本過程參考前面的帖子,實現代碼:

void BubbleSort(int a[], int n)        // 本算法將a[]中的元素從小到到大進行排序
{ for(int i = 0; i < n - 1; i++){ flag = false;                  // 表示本趟冒泡是否發生交換的標志
        for(j = n - 1; j > i; j--){    // 一趟冒泡過程
            swap(a[j - 1], a[j]);      // 為交換函數,將a[j] 與 a[j - 1] 進行交換
            flag = true; } } if (flag == false)                 // 本趟遍歷后沒有發生交換,說明表已經有序
        return; }

注意:

1)通過設置flag可以直接判斷表是否有序,如果有序直接退出,是一個小技巧;

2)排序時間復雜度與表的初始順序有關,表有序時,比較次數為 n - 1,此為最好的時間復雜度O(n),最差為O(n^2);

3)每趟排序將一個元素放在最終的位置上;

 

2、快速排序

基於分治法思想,這個與動態圖不太一樣,針對考研的同學,希望還是以嚴蔚敏的版本為准:

int partition(int a[], int low, int high){ int pivot = a[low];  // 將當前表中第一個元素設為樞軸值
    while(low < high){  // 下標控制條件
        while(low < high && a[high] >= pivot) --high; a[low] = a[high]; while(low < high && pivot >= a[low]) ++low; a[high] = a[low]; } a[low] = pivot; return low; // 返回數組下標, } 

 

 如圖,下面的列子:

一開始的表 a[ ]

while(low < high && a[high] >= pivot) // 如果a[high]> pivot, high向前移動,直到a[high] < pivot; --high; a[low] = a[high];  // 交換

此時的狀態:

        while(low < high && pivot >= a[low]) // 如果a[low] < pivot, low向后移動,直到a[low] > pivot; ++low; a[high] = a[low];  // 交換

此時的狀態:

 

while( low < high) { ... } 的第一遍循環結束,開始第二次的循環:

 

這個小例子比較簡單,兩遍就結束了,接下來判斷條件不滿足,退出:

while(low < high){  // 下標控制條件
 ... }

 

    a[low] = pivot; return low;  // 返回數組下標,

結果圖:

這是各個分支的過程,其中主程序的代碼可以采用遞歸式,也可以采用非遞歸的,

void QuickSort (int a[], int low, int high){ if (low < high){ int pivot = partition(A, low, high); QuickSort(A, low, pivot - 1); QuickSort(A, pivot + 1, high); } /if }

point:

1)快排是所有內排序算法平均性能最優的,平均時間復雜度O(nlog₂n),最差為O(n²),平均空間復雜度O(log₂n),最差為O(n);

2)不穩定;

3)其過程中,不產生有序子序列,但是每趟將一個元素放在最終的位置上,就是基准元素;

 

三、插入排序

1、直接插入排序

這個比較好理解,直接上代碼:

void InsertSort(int a[], int n){ int i, j; for(int i = 2; i <= n; i++){  // 依次將下標2 —— n 插入到已排好的序列
        if (a[i] < a[i -1]){  // 前面下標從2開始就是此處 i - 1 的原因
            a[0] = a[i];  // 
            for(j = i -1; a[0] < a[j]; --j){  // 從后面往前查找待插入的元素
                a[j + 1] = a[j];  // 向后挪位置
 } a[j + 1] = a[0]; } } }

point:

1)移動和比較次數取決於待排序表的初始狀態,最好的情況是表已經有序,時間復雜度為O(n), 平均時間復雜度為O(n²);

2)穩定;

3)注意有個哨兵機制,之后的折半插入與希爾排序也是根據此算法轉換而來;

 

 2、折半插入排序

基於直接插入排序作出的改動,如圖:

當下邊指向7時,前面已經有序,因此利用二分法找到2的后面,然后再直接放入,

point :

1)僅僅是減少了比較的次數,約為O(nlog₂n),該比較次數與待排序表的初始狀態無關,僅取決於表中的元素個數n;移動次數沒有改變,依賴於待排序表中的初始狀態;

2)時間復雜度仍為O(n²)

3)穩定

 

 3、希爾排序

參考的帖子有點小瑕疵,這里面着重提一下:

先上代碼:

void ShellSort(int a[], int n){ for(dk = n/2; dk >= 1; dk = df/2){  // 初始增量序列是n/2,后面依次是
        for(i = dk+1; i<=n; ++i){ if (a[i] < a[i - dk]){ a[0] = a[i];  // 先暫存在a[0]中
                for(j = i-dk; j>0 && a[0]<a[j]; j -= dk){  //j<0 的時候說明到頭了
                    a[j + dk] = a[j]; } a[j + dk] = a[0];  // 記錄后移,查找插入的位置
 } } } }

如圖,初始數組:

 

第一趟,dk = n / 2 時,注意,帶*號的還沒進行比較:

 

此時,重點來了,

        for(j = i-dk; j>0 && a[0]<a[j]; j -= dk){  //j<0 的時候說明到頭了
                    a[j + dk] = a[j];  
        }

這段代碼的作用就是下圖的效果,還要與前面同樣的增量序列大小進行比較

 至此,第一輪循環結束,第二輪增量序列為2 = 4 / 2,之后的過程也是如此,就不重復了。

point:

1)時間復雜度依賴於增量序列的函數,n 取某個值時,最好為O()

2)不穩定

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM