常用排序的實現及比較

排序的穩定性:假設在待排序的序列中存在多個相同關鍵字的記錄,經過排序,這些關鍵字的位置保持不變,則稱這個排序算法是穩定的,否則是不穩定的。
1. 選擇排序(不穩定)
選擇排序是一種直觀的排序算法,每次找到最大的或者最小的數與,存放在序列的起始元素,知道所有的元素都排序結束。選擇排序是不穩定的,假設5,5,3進行排序,3比第一個5小交換位置,導致第一個5摞到第二個5後面,所以選擇排序是不穩定的排序。
以下面5個無序的數據爲例:
56 12 80 91 20
排序過程:
a.12 56 80 91 20
b.12 20 80 91 56
c.12 20 56 91 80
d.12 20 56 80 91
代碼實現:

void select_sort(int *arr,int len)
{
    if( arr == NULL || len < 0)
    {
        return ;
    }
    int i = 0;
    for( i = 0;i < len - 1;i++)
    {
        int min = arr[i];
        int min_index = i;
        for( int j  = i + 1;j < len;j++)
        {
            if( arr[j] < min)
            {
                min = arr[j];
                min_index  = j;
            }
        }
        swap(&arr[i],&arr[min_index]);
    }
}

2.簡單交換排序(穩定排序)
根據序列中兩個關鍵字的比較結果來對換在序列中的位置,交換排序的特點是:將鍵值較大的記錄向序列的尾部移動,鍵值較小的記錄向序列的前部移動。
以下面5個無序的數據爲例:
8 2 1 4 9 5 7 3 6
a.(只細講第一趟)
2 8 1 4 9 5 7 3 6
1 2 8 4 9 5 7 3 6(1是序列中最小的)

void exchange_sort(int *arr,int len)
{
    if( arr == NULL || len < 0)
    {
        return ;
    }
    int i = 0;
    for( i = 0; i < len - 1;i++)
    {
        for( int j = i + 1;j < len ;j++)
        {
            if( arr[i] > arr[j])
            {
                swap(&arr[j],&arr[i]);
            }
        }
    }
}

3.冒泡排序(穩定排序)
冒泡排序是臨近的數字兩兩進行比較,按照從小到大或者從大到小的順序進行交換。
以下面的數字爲例子:
6 2 4 1 5 9
a. 2 6 4 1 5 9
2 4 6 1 5 9
2 4 1 6 5 9
2 4 1 5 6 9
2 4 1 5 6 9
b. 2 1 4 5 6 9
c. 1 2 4 5 6 9

void bubble_sort(int *arr,int len)
{
    if( arr == NULL || len < 0)
    {
        return ;
    }
    for( int i = 0;i < len - 1;i++)
    {
        for( int j = 0;j < len - 1 - i;j++)
        {
            if( arr[j] > arr[j+1])
            {
                swap(&arr[j],&arr[j+1]);
            }
        }
    }
}

假設一組數後面的數是排好序的,則代碼需要進行優化

bool bubble_sort_ex(int *arr,int len)
{
    if( arr == NULL || len < 0)
    {
        return false;
    }
    for( int i = 0;i < len - 1;i++)
    {
        bool flag = true;
        for( int j = 0;j < len - 1 - i;j++)
        {
            if( arr[j] > arr[j+1])
            {
                swap(&arr[j],&arr[j+1]);
                flag = false;
            }
        }
        if( flag )
        {
            break;
        }
    }
    return false;
}

4.插入排序(穩定排序)
有一個已經有序的數據序列,要求在這個已經排好的數據序列中插入一個數,但要求插入後此數據序列仍然有序,這個時候就要用到一種新的排序方法——插入排序法,插入排序的基本操作就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,算法適用於少量數據的排序。
以下列數字爲例:
65 27 59 64 58
剛開始時把65認爲已經排好序
a.插入27
27 65 59 64 58
b.插入59
27 59 65 64 58
c.插入64
27 59 64 65 58
d.插入58
27 58 59 64 65

void insert_sort(int *arr,int len)
{
    if( arr == NULL || len < 0)
    {
        return ;
    }
    int i = 0;
    int j = 0;
    for(i = 1;i < len;i++)
    {
        int temp = arr[i];
        for( j = i - 1; j >= 0;j--)
        {
            if( arr[j] > temp)
            {
                arr[j+1] = arr[j];
            }
            else
            {
                break;
            }
        }
        arr[j+1] = temp;
    }
}

5.希爾排序(不穩定排序)
希爾排序屬於插入排序的一種,也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。把記錄按步長 gap 分組,對每組記錄採用直接插入排序方法進行排序。隨着步長逐漸減小,所分成的組包含的記錄越來越多,當步長的值減小到 1 時,整個數據合成爲一組,構成一組有序記錄,則完成排序。
排序前: 9 1 2 5 7 4 8 6 3 5
gap = 5: 4 1 2 3 5 9 8 6 5 7
gap = 2: 2 1 4 3 5 6 5 7 8 9
gap = 1: 1 2 3 4 5 5 6 7 8 9
排序後: 1 2 3 4 5 5 6 7 8 9

void shell(int *arr,int len, int gap)
{
    int i;
    int j;
    // // 把距離爲 gap 的元素編爲一個組,掃描所有組
    for (i =gap; i<len; i++)
    {
        int tmp = arr[i];  
        for (j=i-gap; j>=0 && arr[j] > tmp; j -= gap) //// 對距離爲 gap 的元素組進行排序
        {
            arr[j+gap] = arr[j];    
        }
        arr[j+gap] = tmp;
    }
}

void shell_sort(int *arr,int len)
{
    for (int i=len/1000 ;i>1; i=i/2)  //計算gap
    {
        shell(arr, len ,i);
    }

    shell(arr, len ,1);
}

6.歸併排序(穩定排序)
歸併排序是建立在歸併操作上的一種有效的排序算法。該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將待排序序列R[0…n-1]看成是n個長度爲1的有序序列,將相鄰的有序表成對歸併,得到n/2個長度爲2的有序表;將這些有序序列再次歸併,得到n/4個長度爲4的有序序列;如此反覆進行下去,最後得到一個長度爲n的有序序列。

static int tmp[20] = {0}
// 把兩個有序的數組排序成一個數組  
 void merge(int *arr, int start, int middle, int end)  
 {  
    int first = start;  
    int second = middle + 1;  
    int index = start;  
    while ((first <= middle) && (second <= end)){  
        if (arr[first] >= arr[second])  
            tmp[index++] = arr[second++];  
        else  
            tmp[index++] = arr[first++];  
    }     
    while(first <= middle) tmp[index++] = arr[first++];  
    while(second <= end) tmp[index++] = arr[second++];  

    for (first = start; first <= end; first++)  
    {
        arr[first] = tmp[first];
    }  
 }  

// 遞歸劃分數組  
 void merge_Sort(int *array, int start, int end)  
 {  
     if (start >= end)  
         return;  
     int middle = ((end + start) >> 1);  
     merge_Sort(array, start, middle);// 遞歸劃分左邊的數組  
     merge_Sort(array, middle+1, end);// 遞歸劃分右邊的數組  
     merge(array, start, middle, end);// 對有序的兩個數組進行合併成一個有序的數組  
 }  

7.堆排序
堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。可以利用數組的特點快速定位指定索引的元素。

void heap_adjust(int *arr, int start, int end)
{
    int i;
    int tmp = arr[start];
    for (i=2*start+1; i<=end; i=2*i+1)
    {
        if (i+1<=end && arr[i] < arr[i+1])
        {
            i++;
        }

        if (tmp < arr[i])
        {
            arr[start] = arr[i];
            start = i;
        }
        else
        {
            break;
        }
    }

    arr[start] = tmp;
}

char *heap_sort(int *arr, int len)  //3/2N * Log2N
{
    //1.從下到上,建立大根堆
    for (int i=(len-2)/2; i>=0; i--)//N/2*log2N
    {
        heap_adjust(arr,i,len-1);
    }

    //2.交換
    swap(&arr[0], &arr[len-1]);

    //3.調整爲大根堆
    for (int i=len-2; i>0 ;i--) //N*log2N
    {
        heap_adjust(arr,0,i);
        swap(&arr[0], &arr[i]);
    }

    return __FUNCTION__;
}

8.快速排序
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。

void partition(int *arr,int L, int R)
{
    if (L >= R)
    {
        return ;
    }

    int left = L;
    int right = R;

    int tmp = arr[L];
    while(L<R)
    {
        while(tmp <= arr[R] && L<R)
        {
            R--;
        }
        arr[L] = arr[R];

        while(arr[L] <= tmp && L<R)
        {
            L++;
        }
        arr[R] = arr[L];
    }
    arr[L] = tmp;

    partition(arr,left,L-1);
    partition(arr,L+1,right);
}
void quick_sort(int *arr, int len)
{
    partition(arr, 0, len-1);
}

對這8種常用排序的時間複雜度和空間複雜度分析
這裏寫圖片描述

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