排序算法的總結

1. 概述

在數據結構中常見的排序算法有八種,大致可歸納以下算法

這些排序算法的性能分析以下圖數組

其中,算法的穩定性是指多個相同值的相對位置也許會在算法結束時產生變更數據結構

2. 排序算法

2.1 交換排序

交換排序的對象是序列中的兩個元素,基本操做是目標元素位置的互換,它的特色是鍵值大的元素向後移動,鍵值小的元素向前移動。ide

交換排序算法主要有兩種:冒泡排序和快速排序性能

2.1.1 冒泡排序

(一)思想spa

 冒泡排序的思想是經過兩層循環遍歷序列,每次遍歷都會使無序區中最大的元素經過不斷地交換直到序列尾部的有序區(就像冒泡同樣,一點一點浮到水面)3d

 

(二)C++實現code

void bubble_sort(int a[], int n)
{
    int i, j, temp;
    for (j = 0; j < n - 1; j++)
        for (i = 0; i < n - 1 - j; i++)
        {
            if(a[i] > a[i + 1])
            {
                temp = a[i];
                a[i] = a[i + 1];
                a[i + 1] = temp;
            }
        }
}

 2.1.2 快速排序

(一)思想對象

經過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的全部數據都比另一部分的全部數據都要小,而後再按此方法對這兩部分數據分別進行快速排序,整個排序過程能夠遞歸進行,以此達到整個數據變成有序序列。blog

(二)C++實現

void Qsort(int arr[], int low, int high){
    if (high <= low) return;
    int i = low;
    int j = high + 1;
    int key = arr[low];
    while (true)
    {
        /*從左向右找比key大的值*/
        while (arr[++i] < key)
        {
            if (i == high){
                break;
            }
        }
        /*從右向左找比key小的值*/
        while (arr[--j] > key)
        {
            if (j == low){
                break;
            }
        }
        if (i >= j) break;
        /*交換i,j對應的值*/
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    /*中樞值與j對應值交換,將其換到中間位置,左邊全部的數都小於它,右邊全部的數都大於它*/
    int temp = arr[low];
    arr[low] = arr[j];
    arr[j] = temp;
    Qsort(arr, low, j - 1);
    Qsort(arr, j + 1, high);
}

2.2 插入排序

插入排序的對象是某個元素,基本操做就是取出元素和插入到某一位置,插入排序的特色是不斷的維護和擴大有序區間,直至完成對整個隊列的排序。

2.2.1 直接插入排序

(一)思想

不斷地從無序區取出元素插入到有序區,並最終完成對整個序列的排序。

(二)C++實現

void insert_sort(int a[], int n)
{
    int i, j, k;
    for (i = 1; i < n; i++)
    {
        //爲a[i]在前面的a[0...i-1]有序區間中找一個合適的位置
        for (j = i - 1; j >= 0; j--)
            if (a[j] < a[i])
                break;
        //如找到了一個合適的位置
        if (j != i - 1)
        {
            //將比a[i]大的數據向後移
            int temp = a[i];
            for (k = i - 1; k > j; k--)
                a[k + 1] = a[k];
            //將a[i]放到正確位置上
            a[k + 1] = temp;
        }
    }
}

2.2.2 希爾(Shell)排序

(一)思想

希爾排序是直接插入排序的改進版本,經過劃分小區間並對小區間使用直接插入排序,並不斷地減少劃分的區間數(每一個區間元素愈來愈多)直至只有一個區間(即原隊列)。

(二)C++實現

void ShellSort(int a[], int n)
 {
        int i,j;
        int jump = n;
        do 
        {
            jump = jump/3 + 1;
            for(i=jump+1;i<n;i++)
            {
                if (a[i]<a[i-jump])
                {
                    a[0] = a[i];
                    for (j=i-jump;j>0&&a[0]<a[j];j-=jump)
                    {
                        a[j+jump] = a[j];
                    }
                    a[j+jump] = a[0];
                }
            }
        } while (jump>1);   
 }

2.3 選擇排序

選擇排序的基本對象是知足性質要求的某元素(如最大元素或最小元素),基本操做是是對此元素的移動,經過對知足要求元素的不斷選擇和移動,實現隊列的排序。

2.3.1 簡單選擇排序

(一)思想

簡單選擇排序每次遍歷待排序隊列(初始爲整個隊列)找到最小元素,放至待排序列的隊首,而後縮小待排序列的長度,直至隊列排序完成。

(二)C++實現

void select_sort(int a[],int n)
{
    int i,j,min;
    for(int i=0;i<n-1;i++)//i爲已排序序列的末尾
    {
        min=i;
        for(int j=i+1;j<n;j++)//未排序序列
            if(a[j]<a[min])//找出未排序序列中的最小值
                min=j;
        if(min!=i)
            swap(a[i],a[min]);//放到已排序序列的末尾,該操做頗有可能把穩定性打亂,因此選擇排序是不穩定的排序算法
    }
}

2.3.2 堆排序

(一)思想

2.3.2.1 堆

堆排序是我認爲排序算法裏面最複雜難懂的一個,首先咱們瞭解一下什麼是堆:

堆是具備如下性質的徹底二叉樹:每一個結點的值都大於或等於其左右孩子結點的值,稱爲大頂堆;或者每一個結點的值都小於或等於其左右孩子結點的值,稱爲小頂堆。以下圖:

同時,對堆中的結點按層進行編號,將這種邏輯結構映射到數組中就是下面這個樣子

該數組從邏輯上講就是一個堆結構,咱們用簡單的公式來描述一下堆的定義就是:

大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

2.3.2.2 堆排序

接下來再來講堆排序

 堆排序的基本思想是:將待排序序列構形成一個大頂堆,此時,整個序列的最大值就是堆頂的根節點。將其與末尾元素進行交換,此時末尾就爲最大值。而後將剩餘n-1個元素從新構形成一個堆,這樣會獲得n個元素的次小值。如此反覆執行,便能獲得一個有序序列了

升序——大頂堆 降序——小頂堆

(二)C++實現

/* 
 * (最大)堆的向下調整算法
 *
 * 注:數組實現的堆中,第N個節點的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
 *     其中,N爲數組下標索引值,如數組中第1個數對應的N爲0。
 *
 * 參數說明:
 *     a -- 待排序的數組
 *     start -- 被下調節點的起始位置(通常爲0,表示從第1個開始)
 *     end   -- 截至範圍(通常爲數組中最後一個元素的索引)
 */
void maxheap_down(int a[], int start, int end)
{
    int c = start;            // 當前(current)節點的位置
    int l = 2*c + 1;        // 左(left)孩子的位置
    int tmp = a[c];            // 當前(current)節點的大小
    for (; l <= end; c=l,l=2*l+1)
    {
        // "l"是左孩子,"l+1"是右孩子
        if ( l < end && a[l] < a[l+1])
            l++;        // 左右兩孩子中選擇較大者,即m_heap[l+1]
        if (tmp >= a[l])
            break;        // 調整結束
        else            // 交換值
        {
            a[c] = a[l];
            a[l]= tmp;
        }
    }
}

/*
 * 堆排序(從小到大)
 *
 * 參數說明:
 *     a -- 待排序的數組
 *     n -- 數組的長度
 */
void heap_sort_asc(int a[], int n)
{
    int i;

    // 從(n/2-1) --> 0逐次遍歷。遍歷以後,獲得的數組其實是一個(最大)二叉堆。
    for (i = n / 2 - 1; i >= 0; i--)
        maxheap_down(a, i, n-1);

    // 從最後一個元素開始對序列進行調整,不斷的縮小調整的範圍直到第一個元素
    for (i = n - 1; i > 0; i--)
    {
        // 交換a[0]和a[i]。交換後,a[i]是a[0...i]中最大的。
        swap(a[0], a[i]);
        // 調整a[0...i-1],使得a[0...i-1]仍然是一個最大堆。
        // 即,保證a[i-1]是a[0...i-1]中的最大值。
        maxheap_down(a, 0, i-1);
    }
}

2.4 歸併排序

(一)思想

歸併排序是創建在歸併操做上的一種有效的排序算法,該算法是採用分治法(Divide and Conquer)的一個很是典型的應用。將已有序的子序列合併,獲得徹底有序的序列;即先使每一個子序列有序,再使子序列段間有序。

(二)C++實現

void merge_sort(int *data, int start, int end, int *result)
 {
     if(1 == end - start)//若是區間中只有兩個元素,則對這兩個元素進行排序
     {
         if(data[start] > data[end])
         {
             int temp  = data[start];
             data[start] = data[end];
             data[end] = temp;
         }
         return;
     }
     else if(0 == end - start)//若是隻有一個元素,則不用排序
         return;
     else
     {
         //繼續劃分子區間,分別對左右子區間進行排序
         merge_sort(data,start,(end-start+1)/2+start,result);
         merge_sort(data,(end-start+1)/2+start+1,end,result);
         //開始歸併已經排好序的start到end之間的數據
         merge(data,start,end,result);
         //把排序後的區間數據複製到原始數據中去
         for(int i = start;i <= end;++i)
             data[i] = result[i];
     }
 }

void merge(int *data,int start,int end,int *result)
{
    int left_length = (end - start + 1) / 2 + 1;//左部分區間的數據元素的個數
    int left_index = start;
    int right_index = start + left_length;
    int result_index = start;
    while(left_index < start + left_length && right_index < end+1)
    {
        //對分別已經排好序的左區間和右區間進行合併
        if(data[left_index] <= data[right_index])
            result[result_index++] = data[left_index++];
        else
            result[result_index++] = data[right_index++];
    }
    while(left_index < start + left_length)
        result[result_index++] = data[left_index++];
    while(right_index < end+1)
        result[result_index++] = data[right_index++];
}

2.5 基數排序

(一)思想

基數排序(Radix Sort)是桶排序的擴展,它的基本思想是:將整數按位數切割成不一樣的數字,而後按每一個位數分別比較。
具體作法是:將全部待比較數值統一爲一樣的數位長度,數位較短的數前面補零。而後,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成之後, 數列就變成一個有序序列。

經過基數排序對數組{53, 3, 542, 748, 14, 214, 154, 63, 616},它的示意圖以下:

(二)C++實現

/*
 * 獲取數組a中最大值
 *
 * 參數說明:
 *     a -- 數組
 *     n -- 數組長度
 */
int get_max(int a[], int n)
{
    int i, max;

    max = a[0];
    for (i = 1; i < n; i++)
        if (a[i] > max)
            max = a[i];
    return max;
}

/*
 * 對數組按照"某個位數"進行排序(桶排序)
 *
 * 參數說明:
 *     a -- 數組
 *     n -- 數組長度
 *     exp -- 指數。對數組a按照該指數進行排序。
 *
 * 例如,對於數組a={50, 3, 542, 745, 2014, 154, 63, 616};
 *    (01) 當exp=1表示按照"個位"對數組a進行排序
 *    (02) 當exp=10表示按照"十位"對數組a進行排序
 *    (03) 當exp=100表示按照"百位"對數組a進行排序
 *    ...
 */
void count_sort(int a[], int n, int exp)
{
    int output[n];             // 存儲"被排序數據"的臨時數組
    int i, buckets[10] = {0};

    // 將數據出現的次數存儲在buckets[]中
    for (i = 0; i < n; i++)
        buckets[ (a[i]/exp)%10 ]++;

    // 更改buckets[i]。目的是讓更改後的buckets[i]的值,是該數據在output[]中的位置。
    for (i = 1; i < 10; i++)
        buckets[i] += buckets[i - 1];

    // 將數據存儲到臨時數組output[]中
    for (i = n - 1; i >= 0; i--)
    {
        output[buckets[ (a[i]/exp)%10 ] - 1] = a[i];
        buckets[ (a[i]/exp)%10 ]--;
    }

    // 將排序好的數據賦值給a[]
    for (i = 0; i < n; i++)
        a[i] = output[i];
}

/*
 * 基數排序
 *
 * 參數說明:
 *     a -- 數組
 *     n -- 數組長度
 */
void radix_sort(int a[], int n)
{
    int exp;    // 指數。當對數組按各位進行排序時,exp=1;按十位進行排序時,exp=10;...
    int max = get_max(a, n);    // 數組a中的最大值

    // 從個位開始,對數組a按"指數"進行排序
    for (exp = 1; max/exp > 0; exp *= 10)
        count_sort(a, n, exp);
}
相關文章
相關標籤/搜索