1. 程式人生 > >排序演算法--幾種插入排序

排序演算法--幾種插入排序

    插入排序總體上說也是簡單排序,除去Shell排序的話,其他的插入排序演算法時間複雜度基本上是O(n ^ 2)的,除非有特殊的情況.

    首先說說直接插入排序:

描述:

    插入排序的思想大概就像是打牌是摸牌的過程(很多書諸如<<演算法導論>>和<<計算機程式設計藝術>>都那這個例子來打比方,好像大師們都喜歡玩橋牌,我不是大師所以只喜歡打夠級).

    玩牌者新摸到牌時總是要搜尋一下然後把牌插到適合的位置,慢慢地,牌摸完時,手中地牌自然而然地排好了順序.

程式碼:

#include <stdio.h>

constint
 MAX =10;

void InsertSort(int*A)
{
    
int i, j, temp;
    
for(i =1; i < MAX; i++)
    
{
        temp 
= A[i];
        j 
= i -1;
        
while( j >=0&& temp < A[j])
        
{
            A[j 
+1= A[j];
            j
--;
        }

        A[j 
+1= temp;
    }

}


int main()
{
    
int A[MAX];
    
int i;
    
int nums = MAX;
    printf(
"Please input %d numbers: ", nums);
    
for(i =0; i < MAX; i++)
        scanf(
"%d"&A[i]);
    InsertSort(A);
    
for(i =0; i < MAX; i++)
        printf(
"%d ", A[i]);
    
return0;
}

分析:

    直接插入排序的好處就是演算法簡單明瞭,程式容易實現.但是效率就不可避免的比較低下.程式除了需要一個記錄資料的陣列以外還要有一個臨時的輔助空間.

    從程式中可以看出,即使假設陣列已經是順序的,程式仍舊需要進行n - 1次的比較,但是不用進行陣列元素的移動.當陣列是逆序的時候,程式需要進行(n + 2)(n - 1) / 2次的比較,另加(n + 4)(n - 1) / 2次的陣列元素移動.綜上平均計算一下的話,差不多要進行(n ^ 2) / 4次的比較和移動,所以複雜度應該是O(n ^ 2).

折半插入排序:

    鑑於直接插入排序再處理第i個數據的時候,平均要和i / 2個已經排好序的資料進行比較,如果有n個元素的話,那麼總的計算起來平均就是(n ^ 2) / 4.我們可以把二分查詢和直接插入排序結合起來.用二分查詢的技術來尋找應該插入的正確位置.

程式碼:

#include <stdio.h>

constint MAX =10;

void BinaryInsertSort(int*A)
{
    
int i, j, temp, first, last, mid;
    
for(i =1; i < MAX; i++)
    
{
        temp 
= A[i];
        first 
=0;
        last 
= i -1;
        
        
while(first <= last) //用二分查詢得方法查詢key應該插入的位置
{
            mid 
= (first + last) /2;
            
if(A[mid] > temp)
                last 
= mid -1;
            
else
                first 
= mid +1;
        }

        
        j 
= i -1;
        
while(j > last && temp < A[j])
        
{
            A[j 
+1= A[j];
            j
--;
        }

        A[last 
+1= temp;
    }

}


int main()
{
    
int A[MAX];
    
int i;
    
int nums = MAX; // Just for the next sentence。
    printf("Please input %d numbers: ", nums);
    
for(i =0; i < MAX; i++)
        scanf(
"%d"&A[i]);
    BinaryInsertSort(A);
    
for(i =0; i < MAX; i++)
        printf(
"%d ", A[i]);
    
return0;
}

    這個方法可以減少資料比較的次數,但是資料移動還是要一步一步的進行(仍舊需要移動大約i / 2個已排好序的元素),所以時間複雜度沒有什麼實質的改善.治標不治本的方法.

二路插入排序:

    上面的二分插入減少的是資料比較的次數,而二路插入的方法可以減少資料移動的次數.

程式碼:

#include <stdio.h>

constint MAX =10;

void twoWaysInsertSort(int*A)
{
    
int first, final, i, keyIndex, index;
    
int B[MAX];           //輔助記錄空間
    first = final =0;
    B[
0= A[0];
    
    
for(i =1; i < MAX; i++)
    
{
        
if(A[i] >= B[final])
        
{
            final 
+=1;
            B[final] 
= A[i];
        }

            
        
elseif(A[i] < B[first])
        
{
            first 
= (first + MAX -1% MAX;
            B[first] 
= A[i];
        }

        
else//當資料在最大值和最小值之間時
{
            keyIndex 
= (final -1+ MAX) % MAX;
            
while(1)
            
{
                
if (B[keyIndex] <= A[i])
                
{
                    index 
= final++;
                    
while(index != keyIndex)
                    
{
                        B[(index 
+1% MAX] = B[index];
                        index 
= (index -1+ MAX) % MAX;
                    }

                    B[(keyIndex 
+1% MAX] = A[i];
                    
break;
                }

                keyIndex 
= (keyIndex -1+ MAX) % MAX;
             }

         }

     }

     
for(i =0; i < MAX; i++)
     
{
        printf(
"%d ", B[i]);
     }

     printf(
"");
     
for(i =0; i < MAX; i++)   //將輔助空間中的排好序的陣列複製到原陣列中
{
        A[i] 
= B[first];
        first 
= (first +1% MAX;
     }

        
}


int main()
{
    
int A[MAX];
    
int i;
    
int nums = MAX; // Just for the next sentence。
    printf("Please input %d numbers: ", nums);
    
for(i =0; i < MAX; i++)
        scanf(
"%d"&A[i]);
    twoWaysInsertSort(A);
    
for(i =0; i < MAX; i++)
        printf(
"%d ", A[i]);
    
return0;
}

    這個二路插入的演算法可以比二分插入演算法節約一半的執行時間,但是程式比較複雜.

    上面的程式另外使用了一個大小為n的輔助空間,並且把B看成了迴圈向量.first是陣列中最小的元素,final是陣列中最大的元素,如果待排序的元素小於first則放在first的前面,如果比final小則放在final的後面.

    在<<計算機程式設計藝術>>中作者說到過一種方法,把輸入區域當作一個迴圈表,而且位置N同1相鄰.根據上一次插入的元素是落在已排序元素的中心的左面還是右面,而從當前未排序元素段的右面或是左面來取新的元素.過後通常需要"轉動"這個區域.通過這個方法可以僅用N + 1的空間同時進行輸入,輸出和排序.

    插入排序族中還有許多其他的方法,但是基本上都是在直接插入的方法上改進的,目的無非是減少比較和移動的次數.Shell排序也可以算是一種插入排序.不同的是Shell排序的時間複雜度不是O(n ^ 2),而是根據所選的增量序列而定的.