插入排序、希爾排序、冒泡排序、快速排序、選擇排序、堆排序、歸併排序

插入排序方法:時間複雜度O(n^2)的穩定排序:
每步將一個待排序的紀錄,按其關鍵碼值的大小插入前面已經排序的文件中適當位置上,直到全部插入完爲止。

直接插入排序的算法思路:
(1) 設置監視哨r[0],將待插入紀錄的值賦值給r[0];
(2) 設置開始查找的位置j;
(3) 在數組中進行搜索,搜索中將第j個紀錄後移,直至r[0].key≥r[j].key爲止;
(4) 將r[0]插入r[j+1]的位置上。
代碼:void Insert_sort(int n)
         {
        /* 對數組R中的記錄R[1..n]按遞增序進行插入排序  */
int i,j;
for(i=2;i<=n;i++) /* 依次插入R[2],…,R[n] */
    if(R[i]<R[i-1])
    {/* 若R[i]大於等於有序區中所有的R,則R[i] */
        /* 應在原有位置上 */
        R[0]=R[i];j=i-1; /* R[0]是哨兵,且是R[i]的副本 */
        do{ /* 從右向左在有序區R[1..i-1]中查找R[i]的插入位置 */
            R[j+1]=R[j]; /* 將關鍵字大於R[i]的記錄後移 */
            j--;
        }while(R[0]<R[j]);  /* 當R[i]≥R[j]時終止 */
        R[j+1]=R[0]; /* R[i]插入到正確的位置上 */
    }
          }

希爾排序法(縮小增量排序):時間複雜度與增量序列的選取有關,下限是n*log2n不穩定排序
代碼:
    void ShellPass(int d, int n)
{/* 希爾排序中的一趟排序,d爲當前增量 */
    int i,j;
    for(i=d+1;i<=n;i++) /* 將R[d+1..n]分別插入各組當前的有序區 */
        if(R[i]<R[i-d])
        {
            R[0]=R[i];j=i-d; /* R[0]只是暫存單元,不是哨兵 */
            do {/* 查找R[i]的插入位置 */
                R[j+d]=R[j];/* 後移記錄 */
                j=j-d; /* 查找前一記錄 */
            }while(j>0&&R[0]<R[j]);
            R[j+d]=R[0]; /* 插入R[i]到正確的位置上 */
        } /* endif */
} /* end of ShellPass */

void  Shell_Sort(int n)
{
    int increment=n; /* 增量初值,不妨設n>0 */
    do {
        increment=increment/3+1; /* 求下一增量 */
        ShellPass(increment,n); /* 一趟增量爲increment的Shell插入排序 */
    }while(increment>1);
} /* ShellSort */


冒泡排序法
void Bubble_sort(int n)
{
    int i,j;
    int exchange; /* 交換標誌 */
    for(i=1;i<n;i++){ /* 最多做n-1趟排序 */
        exchange=0; /* 本趟排序開始前,交換標誌應爲假 */
        for(j=n-1;j>=i;j--) /* 對當前無序區R[i..n]自下向上掃描 */
            if(R[j+1]<R[j]){/* 交換記錄 */
                R[0]=R[j+1]; /* R[0]不是哨兵,僅做暫存單元 */
                R[j+1]=R[j];
                R[j]=R[0];
                exchange=1; /* 發生了交換,故將交換標誌置爲真 */
            }
            if(!exchange) /* 本趟排序未發生交換,提前終止算法 */
                return;
    }
}

快速排序法平均複雜度O(nlgn),不是穩定排序
基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列

1)設置兩個變量i、j,排序開始的時候:i=0,j=N-1;
2)以第一個數組元素作爲關鍵數據,賦值給key,即key=A[0];
3)從j開始向前搜索,即由後開始向前搜索(j--),找到第一個小於key的值A[j],將A[j]和A[i]互換;
4)從i開始向後搜索,即由前開始向後搜索(i++),找到第一個大於key的A[i],將A[i]和A[j]互換;
5)重複第3、4步,直到i=j; (3,4步中,沒找到符合條件的值,即3中A[j]不小於key,4中A[i]不大於key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到爲止。找到符合條件的值,進行交換的時候i, j指針位置不變。另外,i==j這一過程一定正好是i+或j-完成的時候,此時令循環結束)。

代碼:
int Partition(int i,int j)
{/* 調用Partition(R,low,high)時,對R[low..high]做劃分,*/
    /* 並返回基準記錄的位置 */
    int pivot=R[i]; /* 用區間的第1個記錄作爲基準 */
    while(i<j){ /* 從區間兩端交替向中間掃描,直至i=j爲止 */
        while(i<j&&R[j]>=pivot) /* pivot相當於在位置i上 */
            j--;  /* 從右向左掃描,查找第1個關鍵字小於pivot.key的記錄R[j] */
        if(i<j) /* 表示找到的R[j]的關鍵字<pivot.key  */
            R[i++]=R[j]; /* 相當於交換R[i]和R[j],交換後i指針加1 */
        while(i<j&&R[i]<=pivot) /* pivot相當於在位置j上*/
            i++; /* 從左向右掃描,查找第1個關鍵字大於pivot.key的記錄R[i] */
        if(i<j) /* 表示找到了R[i],使R[i].key>pivot.key */
            R[j--]=R[i]; /* 相當於交換R[i]和R[j],交換後j指針減1 */
    } /* endwhile */
    R[i]=pivot; /* 基準記錄已被最後定位*/
    return i;
} /* end of partition  */

void Quick_Sort(int low,int high)
{ /* 對R[low..high]快速排序 */
    int pivotpos; /* 劃分後的基準記錄的位置 */
    if(low<high){/* 僅當區間長度大於1時才須排序 */
        pivotpos=Partition(low,high); /* 對R[low..high]做劃分 */
        Quick_Sort(low,pivotpos-1); /* 對左區間遞歸排序 */
        Quick_Sort(pivotpos+1,high); /* 對右區間遞歸排序 */
    }
} /* end of Quick_Sort */

選擇排序法
void Select_Sort(int n)
{
    int i,j,k;
    for(i=1;i<n;i++)
    {/* 做第i趟排序(1≤i≤n-1) */
        k=i;
        for(j=i+1;j<=n;j++) /* 在當前無序區R[i..n]中選key最小的記錄R[k] */
            if(R[j]<R[k])
                k=j; /* k記下目前找到的最小關鍵字所在的位置 */
        if(k!=i)
        { /* 交換R[i]和R[k] */
            R[0]=R[i]; R[i]=R[k]; R[k]=R[0]; /* R[0]作暫存單元 */
        } /* endif */
    } /* endfor */
} /* end of Select_Sort */

堆排序法(完全二叉樹)
void Heapify(int s,int m)
{ /*對R[1..n]進行堆調整,用temp做暫存單元 */
    int j,temp;
    temp=R[s];
    j=2*s;
    while (j<=m)
    {
        if (R[j]>R[j+1]&&j<m) j++;
        if (temp<R[j]) break;
        R[s]=R[j];
        s=j;
        j=j*2;
    }/* end of while */
    R[s]=temp;
} /* end of Heapify */

void BuildHeap(int n)
{ /* 由一個無序的序列建成一個堆 */
    int i;
    for(i=n/2;i>0;i--)
        Heapify(i,n);
}


void Heap_Sort(int n)
{ /* 對R[1..n]進行堆排序,不妨用R[0]做暫存單元 */
    int i;
    BuildHeap(n); /* 將R[1-n]建成初始堆 */
    for(i=n;i>1;i--)
    { /* 對當前無序區R[1..i]進行堆排序,共做n-1趟。 */
        R[0]=R[1]; R[1]=R[i];R[i]=R[0]; /* 將堆頂和堆中最後一個記錄交換 */
        Heapify(1,i-1); /* 將R[1..i-1]重新調整爲堆,僅有R[1]可能違反堆性質 */
    } /* end of for */
} /* end of Heap_Sort */

歸併排序(穩定排序)
歸併操作的工作原理如下:
第一步:申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列
第二步:設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置
第三步:比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置
重複步驟3直到某一指針超出序列尾
將另一序列剩下的所有元素直接複製到合併序列尾

void Merge(int low,int m,int high)
{/* 將兩個有序的子文件R[low..m)和R[m+1..high]歸併成一個有序的 */
    /* 子文件R[low..high] */
    int i=low,j=m+1,p=0; /* 置初始值 */
    int *R1; /* R1是局部向量,若p定義爲此類型指針速度更快 */
    R1=(int *)malloc((high-low+1)*sizeof(int));
    if(!R1) /* 申請空間失敗 */
    {
        puts("Insufficient memory available!");
        return;
    }
    while(i<=m&&j<=high) /* 兩子文件非空時取其小者輸出到R1[p]上 */
        R1[p++]=(R[i]<=R[j])?R[i++]:R[j++];
    while(i<=m) /* 若第1個子文件非空,則複製剩餘記錄到R1中 */
        R1[p++]=R[i++];
    while(j<=high) /* 若第2個子文件非空,則複製剩餘記錄到R1中 */
        R1[p++]=R[j++];
    for(p=0,i=low;i<=high;p++,i++)
        R[i]=R1[p];/* 歸併完成後將結果複製回R[low..high] */
} /* end of Merge */


void Merge_SortDC(int low,int high)
{/* 用分治法對R[low..high]進行二路歸併排序 */
    int mid;
    if(low<high)
    {/* 區間長度大於1 */
        mid=(low+high)/2; /* 分解 */
        Merge_SortDC(low,mid); /* 遞歸地對R[low..mid]排序 */
        Merge_SortDC(mid+1,high); /* 遞歸地對R[mid+1..high]排序 */
        Merge(low,mid,high); /* 組合,將兩個有序區歸併爲一個有序區 */
    }
}/* end of Merge_SortDC */

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章