排序算法

若序列中i.key == j.key(即序列中两个记录的关键字相等),在排列之前i<j;若排列之后仍然有i<j,则称该排序方法是稳定的。反之,若排序之后i可能大于j,则称排序算法是不稳定的。

直接插入排序

基本思想:将一个记录插入到已经排好序的有序表中。
稳定 时间复杂度: O ( n 2 ) O(n^{2}) O(n2)
代码:

void InsertSort(int num[], int n)
{
    for(int i=1; i<n; i++){
        int v = num[i];
        int j = i-1;
        for(; j>=0 && num[j]>v; j--){
            num[j+1] = num[j];
        }
        num[j+1] = v;
    }
}

折半的插入排序代码:

void BInsertSort(int num[], int n)
{
    for(int i=1; i<n; i++){
        int v = num[i];
        int low = 0;
        int high = i-1;
        while(low<=high){
            int m = (low+high)>>1;
            if(num[m]>v){
                high = m-1;
            }
            else{
                low = m+1;
            }
        }
        for(int j=i-1; j>=high+1; j--){
            num[j+1] = num[j];
        }
        num[high+1] = v;
    }
}

希尔排序

基本思想:先将整个待排记录序列分割成若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
不稳定 时间复杂度和选取的增量序列有关
代码:

void ShellSort(int num[], int n)
{
    for(int dk=n/2; dk>=1; dk/=2){
        for(int i=dk+1; i<n; i++){
        int v = num[i];
        int j = i-dk;
        for(; j>=0 && num[j]>v; j-=dk){
            num[j+dk] = num[j];
        }
        num[j+dk] = v;
    }
    }
}

快速排序

基本思想:通过一趟排序将待排记录分割成两个独立的部分,其中一部分记录的关键字比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
不稳定 时间复杂度:平均 O ( n l o g n ) O(nlogn) O(nlogn),最坏 O ( n 2 ) O(n^{2}) O(n2)
代码:

int Partition(int num[], int low, int high)
{
    int key = num[low];
    while(low<high){
        while(low<high && num[high]>=key){
            high--;
        }
        num[low] = num[high];
        while(low<high && num[low]<=key){
            low++;
        }
        num[high]=num[low];
    }
    num[low] = key;
    return low;
}

void QuickSort(int num[], int low, int high)
{
    if(low<high){
        int part = Partition(num, low, high);
        QuickSort(num, low, part-1);
        QuickSort(num, part+1, high);
    }
}

选择排序

基本思想:每一趟在n-i+1个记录中选取关键字最小的记录作为有序序列中第i个记录。
不稳定 时间复杂度 O ( n 2 ) O(n^{2}) O(n2)
代码:

void SelectSort(int num[], int n)
{
    for(int i=0; i<n; i++){
        int pos = i;
        for(int j=i+1; j<n; j++){
            if(num[pos]>num[j]){
                pos = j;
            }
        }
        if(pos != i){
            int temp = num[i];
            num[i] = num[pos];
            num[pos] = temp;
        }
    }
}

堆排序

基本思想:将一维数组看做二叉树,先建立一个大顶堆,将得到的最大关键字的记录和序列中第一个记录交换。然后对序列中前n-1个记录进行筛选,重新调整成大顶堆。如此反复直至排序结束。
不稳定 时间复杂性 O ( n l o g n ) O(nlogn) O(nlogn)
代码:

void HeapAdjust(int num[], int s, int m)
{
    int key = num[s];
    for(int j=2*s; j<m-1; j*=2){
        if(j<m-1 && num[j]<num[j+1]){
            j++;
        }
        if(key<num[j]){
            num[s] = num[j];
            s = j;
        }
        else{
            break;
        }
    }
    num[s] = key;
}

void HeapSort(int num[], int n)
{
    for(int i=n/2-1; i>=0; i--){
        HeapAdjust(num,i,n);
    }
    for(int i=n-1; i>0; i--){
        int temp = num[0];
        num[0] = num[i];
        num[i] = temp;
        HeapAdjust(num,0,i);
    }
}

归并排序

基本思想:分治法。假设初始序列有n个记录,则可看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,知道得到一个长度为n的有序序列。
稳定 时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
代码:

void Merge(int num[],int temp[], int low, int mid, int high)
{
    int i = low;
    int j = mid+1;
    int k = 0;
    while(i<=mid && j<=high){
        if(num[i]<=num[j]){
            temp[k++] = num[i++];
        }
        else{
            temp[k++] = num[j++];
        }
    }
    while(i<=mid){
        temp[k++] = num[i++];
    }
    while(j<=high){
        temp[k++] = num[j++];
    }
    for(i=0; i<k; i++){
        num[low+i] = temp[i];
    }
}

void MergeSort(int num[], int temp[], int low, int high)
{
    if(low<high){
        int mid = (low+high)/2;
        MergeSort(num,temp,low,mid);
        MergeSort(num,temp,mid+1,high);
        Merge(num,temp,low,mid,high);
    }
}

总结

排序方法平均时间最坏情况辅助存储
简单排序 O ( n 2 ) O(n^{2}) O(n2) O ( n 2 ) O(n^{2}) O(n2) O ( 1 ) O(1) O(1)
快速排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n 2 ) O(n^{2}) O(n2) O ( l o g n ) O(logn) O(logn)
堆排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn) O ( 1 ) O(1) O(1)
归并排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn) O ( n ) O(n) O(n)
点赞