1. 程式人生 > >經典演算法之三:插入排序及二分優化

經典演算法之三:插入排序及二分優化

直接插入排序(Insertion Sort)的基本思想是:每次將一個待排序的記錄,按其關鍵字大小插入到前面已經排好序的子序列中的適當位置,直到全部記錄插入完成為止。

設陣列為a[0…n-1]。

1.      初始時,a[0]自成1個有序區,無序區為a[1..n-1]。令i=1

2.      將a[i]併入當前的有序區a[0…i-1]中形成a[0…i]的有序區間。

3.      i++並重復第二步直到i==n-1。排序完成。

直接插入排序

基本思想

當插入第i(i1)個物件時,前面的V[0],V[1],,V[i1]已經排好序。這時,用V[i]的排序碼與V[i1],V[i2

],,V[0]的排序碼順序進行比較,找到插入位置即將V[i]插入,原來位置上的物件向後順移。

直接插入排序圖示

這裡寫圖片描述

從上到下,分別展示了直接排序演算法的所有可能的過程,包括相同排序碼的排序方式(保持了原來的順序,說明是穩定排序)以及in-place操作中的元素移動等。

這裡寫圖片描述

直接插入排序演算法分析

設待排序物件個數為n,則該演算法的主程式執行n1排序碼比較次數和物件移動次數與物件排序碼的初始排列有關

  • 最好情況下,排序前物件已經按照要求的有序。比較次數(KCN):n1 ; 移動次數(RMN):為0。則對應的時間複雜度為O(n)
  • 最壞情況下,排序前物件為要求的順序的反序。第i
    趟時第i個物件必須與前面i個物件都做排序碼比較,並且每做1次比較就要做1次資料移動(具體可以從下面給出的程式碼中看出)。比較次數(KCN):n1i=1i=n(n1)2n22 ; 移動次數(RMN):為n1i=1i=n(n1)2n22。則對應的時間複雜度為O(n2)
  • 如果排序記錄是隨機的,那麼根據概率相同的原則,在平均情況下的排序碼比較次數和物件移動次數約為n24,因此,直接插入排序的時間複雜度O(n2)

直接插入排序演算法的特點

  • 它是穩定排序,不改變相同元素原來的順序。
  • 它是in-place排序,只需要O(1)的額外記憶體空間。
  • 它是線上排序,可以邊接收資料邊排序。
  • 它跟我們牌撲克牌的方式相似。
  • 對小資料集是有效的。

演算法分析

直接插入排序的演算法效能


時間複雜度
 

當資料正序時,執行效率最好,每次插入都不用移動前面的元素,時間複雜度為O(N)。 

當資料反序時,執行效率最差,每次插入都要前面的元素後移,時間複雜度為O(N2)

所以,資料越接近正序,直接插入排序的演算法效能越好。 

空間複雜度

由直接插入排序演算法可知,我們在排序過程中,需要一個臨時變數儲存要插入的值,所以空間複雜度為 1 。

演算法穩定性

直接插入排序的過程中,不需要改變相等數值元素的位置,所以它是穩定的演算法。 

完整參考程式碼

直接插入排

完整程式碼:
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5;
void Init(int *a,int *b,int n)
{
    srand(time(NULL));
    for(int i=0; i<n; i++)
        b[i]=a[i]=rand()%100;
}
void P(int *a,int n)
{
    for(int i=0; i<n; i++)
        cout<<a[i]<<"  ";
    cout<<endl;
}
void Insert_Sort(int *a,int n)
{
    int i,j;
    for(i=1; i<n; i++)
    {
        if(a[i]<a[i-1])
        {
            int t=a[i];
            for(j=i-1; j>=0&&a[j]>t; j--)
            {
                a[j+1]=a[j];
            }
            a[j+1]=t;
        }
        //P(a,i);
    }
}
//二分優化
void Insert_Sort_Binary(int *a,int n)
{
    for(int i=1; i<n; i++)
    {
        if(a[i]<a[i-1])
        {
            int left=0;
            int right=i-1;
            int t=a[i];
            while(left<=right)
            {
                int mid=(left+right)>>1;
                if(a[mid]>t)
                    right=mid-1;
                else
                    left=mid+1;
            }
            for(int j=i-1; j>=left; j--)
            {
                a[j+1]=a[j];
            }
            a[left]=t;
        }
        //P(a,i);
    }
}
int main()
{
    int a[MAXN],b[MAXN];
    char ch;
    do
    {
        Init(a,b,MAXN);
        //P(a,MAXN);
        time_t t1=clock();
        Insert_Sort(a,MAXN);
        time_t t2=clock();
        cout<<t2-t1<<endl;
        //P(a,MAXN);
        //P(b,MAXN);
        time_t t3=clock();
        Insert_Sort_Binary(b,MAXN);
        time_t t4=clock();
        cout<<t4-t3<<endl;
        //P(b,MAXN);
    }
    while(ch=getchar());

    return 0;
}