1. 程式人生 > 其它 >排序演算法詳解

排序演算法詳解

1. 直接插入排序(有序表的擴大)

void InsertSort(int *A,int n){
    int i,j;
    for(i=2;i<=n;i++){   //從第二個元素開始遍歷n-1次,插入到前面的有序陣列中
        A[0]=A[i];      //儲存待插入元素
        for(j=i-1;A[0]<A[j];--j)  
            A[j+1]=A[j];    //將大於待插入元素的元素往後移動一位,空出插入位置
        A[j+1]=A[0];        //插入元素
    }
}
折半插入排序
void InsertSort(int *A,int n){
    int i,j,low,high,mid;
    for(i=2;i<=n;i++){   //從第二個元素開始遍歷n-1次,插入到前面的有序陣列中
        A[0]=A[i];      //儲存待插入元素
        low=1;high=i-1;//有序陣列的左右兩端指標
        while(low<=high){ //折半查找出待插入位置
            mid=(low+high)/2;
            if(A[mid]>A[0]) high=mid-1;
            else low=mid+1;
        }
        for(j=i-1;j>=high+1;--j)  //跳出條件直接使用了插入位置high
            A[j+1]=A[j];    //將大於待插入元素的元素往後移動一位,空出插入位置
        A[j+1]=A[0];        //插入元素
    }
}
希爾排序
void InsertSort(int *A,int n){
    int i,j;
    for(dk=n/2;dk>=1;dk=dk/2)    //步長每次縮小一半直至變為1
        for(i=dk+1;i<=n;i++){   //從第1+dk個元素開始遍歷,插入到前面的有序非連續陣列中
            if(A[i]<A[i-dk]){  //有序則跳過該次迴圈
                A[0]=A[i];      //儲存待插入元素
                for(j=i-dk;j>0&&A[0]<A[j];j-=dk)  //變換的步長為dk
                    A[j+dk]=A[j];    //將大於待插入元素的元素往後移動dk位,空出插入位置
                A[j+dk]=A[0];        //插入元素
            }
        }
}

2. 氣泡排序(逐個冒出最值)

void BubbleSort(int *A,int n){
    for(int i=0;i<n-1;i++){  //遍歷n-1趟
        flag = false;       //設定標誌位,當陣列有序即一趟沒發生交換時,跳出迴圈
        for(int j=n-1;j>i;j--){ //從後往前交換
            if(A[j-1]>A[j]){    //冒出最小值
                swap(A[j-1],A[j]);
                flag=true;
            }
        }
        if(flag==flase) return;
    }
}

3. 快速排序(樞軸遞迴分治)

void QuickSort(int *A,int low,int high){
    if(low>=high) return;
    int pivot_pos=Partition(A,low,high); //使樞軸右側大於左側並返回樞軸位置
    QuickSort(A,low,pivot_pos-1);   //遞迴樞軸左半邊
    QuickSort(A,pivot_pos+1,high);  //遞迴樞軸右半邊
}

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&&A[low]<=pivot) ++low;//移動左端指標至大於樞軸的元素
        A[high]=A[low]; //將該元素換至左端
    }
    A[low]=pivot;//樞軸元素放回樞軸位置
    return low;//返回樞軸位置
}

4. 簡單選擇排序(選出最值)

void SelectSort(int *A,int n){
    for(int i=0;i<n-1;i++){     //從前往後,進行n-1趟遍歷
        min = i;                //記錄這趟遍歷最小值位置
        for(j=i+1;j<n;j++)      //選出該趟遍歷最小元素
            if(A[j]<A[min]) min=j; //選出最小值
        if(min!=i) swap(A[i],A[min]); //如果最小元素不是待插入位置,則交換
    }
}

5. 堆排序(利用堆的性質不斷調整和冒出最值)

void HeapSort(int *A,int len){
    BuildMaxHeap(A,len);    //建立大根堆
    for(int i=len;i>1;i--){  //n-1趟交換和建堆過程
        swap(A[i],A[1]);     //輸出堆頂元素(和堆底元素交換)
        HeadAdjust(A,1,i-1); //把剩餘i-1個元素調整成堆
    }
}

void BuildMaxHeap(int *A,int len){
    for(int i=len/2;i>0;i--)        //從i=~n/2~1,反覆調整堆
        HeadAdjust(A,i,len);       //調整演算法把左右子樹皆為堆的樹調整成堆
}

void HeadAdjust(int *A,int k,int len){
//將元素k為根的子樹進行調整
    A[0]=A[k];      //A[0]暫存子樹根節點
    for(int i=2*k;i<=len;i*=2){      //該迴圈是為了將根節點值不斷往下送
        if(i<len&&A[i]<A[i+1])     i++;  //若右節點大,指標移動到右節點
        if(A[0]>A[i]) break;  //調整結束,即根節點大於兩子節點
        else{               //否則繼續將根節點的值往下送
            A[k]=A[i];      //根節點位置換成較大子節點的值
            k=i;            //記錄子節點指標作為新的根節點或終止節點
        }
    }
    A[k]=A[0]; //迴圈結束終止節點賦往下送根節點值
}
    

6. 歸併排序(分治歸併有序子表)

void MergeSort(int *A,int low,int high){
    if(low>=high) return;
    int mid = (low+high)/2; //陣列分成兩部分
    MergeSort(A,low,mid);   //對左邊遞迴排序使其有序
    MergeSort(A,mid+1,high); //對右邊遞迴排序使其有序
    Merge(A,low,mid,high);   //合併兩有序表
}
void Merge(int *A,int low,int mid,int high){
    for(int k=low;k<=high;k++){
        B[k]=A[k];      //複製出一個新的陣列作為比較
    for(int i=low,j=mid+1,k=i;i<=mid&&j<=high;k++){ //從左往右,兩個指標對兩陣列逐個對比
        if(B[i]<B[j]) A[k]=B[i++];  //將小的值賦回給A,並移動指標
        else A[k]=B[j++];
    }

    while(i<=mid) A[k++]=B[i++]; //將沒複製完的複製過去
    while(j<=high) A[k++]=B[j++]; //將沒複製完的複製過去
    }
}