經常使用排序算法總結

1.冒泡排序算法

要說冒泡應該是很是簡單的一種排序了,思路就如其名,數據像是泡泡同樣逐個上升。shell

/*
 * 冒泡排序
 */
void bubbleSort(int *array , int length)
{
    //設置flag來標示是否已經有序,用於優化冒泡排序
    int flag;
    //共執行n-1趟
    for(int i = 0 ; i < length - 1; i++)
    {
        //默認爲已經有序
        flag = 0;
        //從最後一個元素開始判斷,由於每一趟都會排好一個元素,因此每一趟都會少比較一次
        for(int j = length - 1 ; j > i ; j--)
        {
            //比較是否比前面的元素小
            if(array[j-1] > array[j])
            {
                //若是小就交換位置而且標示爲非有序數組
                swip(&array[j-1], &array[j]);
                flag = 1;
            }
        }
        //若是已經默認有序就不在進行
        if(!flag)
            break;
    }
}

這裏說一下標示flag,若是已經有序好比一、二、三、四、五、6數組,可是內層循環依然會執行,只是不交換元素而已。當執行到沒有元素交換的時候也就說明該數組已經有序了,這時就能夠退出循環。數組

 

2.簡單選擇排序優化

簡單選擇排序就是先找當前位置爲最小,判斷後面是否有比當前元素小的,若是存在就和當前元素交換位置。相比冒泡排序簡單選擇的比較次數較多而交換最多就n-1次。ui

/*
 * 簡單選擇排序
 */
void selectSort(int *array, int length)
{
    //和冒泡排序同樣,也是執行n-1次
    for(int i = 0 ; i < length-1 ; i++)
    {
        //猜想當前位置爲最小元素
        int min = i;
        //執行n-i-1次,用於找到最小的元素
        for(int j = i+1 ; j < length ;j++)
        {
            //若是當前元素比min位置小,那麼當前位置爲min
            if(array[min] > array[j])
                min = j;
        }
        //若是min的位置發生變化,即當前位置不是min,那麼交換位置
        if(min != i)
            swip(&array[min], &array[i]);
    }
}

 

3. 直接插入排序spa

直接插入排序思路也很簡單,就很少說了。code

/*
 * 直接插入排序
 */
void insertSort(int *array ,int length)
{
    //有幾個元素就執行幾回,默認最少兩個元素,從第二個元素開始
    for(int i = 1 ; i < length ; i++)
    {
        //若是當前位置比前一位置元素小
        if(array[i] < array[i-1])
        {
            //保存當前元素
            int temp = array[i];
            int j;
            //當前元素前面的全部比當前元素大的元素所有後移一位
            for(j = i-1 ; array[j] > temp && j >= 0; j--)
            {
                array[j+1] = array[j];
            }
            //把空出來的位置賦上保存的元素
            array[j+1] = temp;
        }
    }
}

 

4.希爾排序blog

希爾排序就是直接插入排序的一個升級,讓序列先相對有序,而後在不斷減少間隔從新分組使整個有序。排序

/*
 * 希爾排序
 */
void shellSort(int *array ,int length)
{
    //設置希爾排序的間隔(每次/2)
    int increment = length/2;
    //當間隔變爲0時結束
    while(increment >= 1)
    {
        //從第一個間隔位置開始,到最後,分好組對每一組進行直接插入排序
        for(int i = increment ; i < length ; i++)
        {
            //和直接插入排序相同,只是間隔從直接插入的1變爲了increment
            if(array[i] < array[i-increment])
            {
                int j;
                int temp = array[i];
                for(j = i - increment ; array[j] > temp && j >= 0 ; j -= increment)
                {
                    array[j+increment] = array[j];
                }
                array[j+increment] = temp;
            }
        }
        //更新間隔
        increment /= 2;
    }
}

 

5.堆排序遞歸

堆排序相對複雜一些,主要思路就是把線性表當作徹底二叉樹去處理,而後構造大頂堆。

/*
 * 調整堆結構
 */
void heepAdjust(int *array, int loc , int length)
{
    //保存當前位置數據
    int temp = array[loc];
    //獲得左子節點位置,每次都獲得子節點
    for(int i = loc * 2 ; i <= length ; i *= 2)
    {
        //若是當前位置<長度,就說明父節點有兩個子節點,若是等於長度就只有一個
        //若是有兩個,把位置調整到較大的位置
        if(i < length && array[i] < array[i+1])
            i++;
        //判斷父節點是否比較大的大
        if(temp > array[i])
            break;
        //把較大的賦值給父節點
        array[loc] = array[i];
        //移動父節點位置到當前節點
        loc = i;
    }
    //最終位置賦值爲保存的數據
    array[loc] = temp;
}

/*
 * 堆排序
 * array 中的0號元素空過去,因此length是array長度-1
 */
void heepSort(int *array , int length)
{
    //長度/2獲得最後一個有子節點的節點位置,循環到根節點
    for(int i = length / 2 ; i > 0 ; i--)
    {
        //調整堆結構,始終保持大頂堆
        heepAdjust(array, i, length);
    }
    //從最後一個元素開始,逐個和根節點交換並調整堆結構
    for(int i = length ; i > 1 ; i--)
    {
        swip(&array[1], &array[i]);
        heepAdjust(array, 1, i-1);
    }
}

 

6.歸併排序

歸併排序應該算是最很差理解的了,歸併排序分爲拆分遞歸和合並回朔兩個過程,其中在拆分的時候會創建一個數組用於保存下一層的回朔。

遞歸實現:

/*
 * 合併排序當前層的數據
 */
void Merge(int SR[], int TR[], int l , int m , int r)
{
    //左邊的初始位置
    int i = l;
    //右邊的初始位置
    int j = m+1;
    //TR的位置
    int k = l;
    //若是比較排序沒有完成就循環
    while(i <= m && j <= r)
    {
        //由於左右相對有序,因此只須要每次找左邊和右邊較小的,而後讓找的位置+1
        if(SR[i] < SR[j])
        {
            TR[k] = SR[i];
            i++;
        }
        else
        {
            TR[k] = SR[j];
            j++;
        }
        //保存的數組位置+1
        k++;
    }
    //把剩下沒有歸併的數據所有存入TR
    if(i <= m)
    {
        for(int n = 0 ; n <= m - i ; n++)
        {
            TR[k] = SR[i+n];
            k++;
        }
    }
    if(j <= r)
    {
        for(int n = 0; n <= r - j ; n++)
        {
            TR[k] = SR[j+n];
            k++;
        }
    }
}
/*
 * 遞歸歸併過程
 */
void MSort(int SR[], int TR1[] ,int l , int r)
{
    //定義一個數組用來存放該層歸併的結果
    int TR2[20] = {0};
    //若是已經不能在拆分,把數據賦值給TR1回朔,注意這裏TR1回朔後就是上一層的TR2了
    if(l == r)
    {
        TR1[l] = SR[l];
    }
    else
    {
        //歸併的界限計算
        int m = (l+r)/2;
        //拆分左右,TR2是回朔合併用的
        MSort(SR, TR2, l, m);
        MSort(SR, TR2, m+1, r);
        //把該層的TR2從新排序並賦值給上一層的TR2
        Merge(TR2 ,TR1, l, m, r);
    }
}

void MergeSort(int *array, int length)
{
    MSort(array, array, 0 ,length - 1);
}

非遞歸實現:Merge方法和遞歸版的同樣

void MergePass(int SR[], int TR[], int k, int length)
{
    int i;
    //判斷是否還有間隔能容納數據,也就是判斷當最後不夠兩組的時候結束
    for(i = 0 ; i < length-2*k+1 ; i+=2*k)
    {
        //合併
        Merge(SR, TR, i, i+k-1, i+2*k-1);
    }
    //判斷是否是在1組合2組數據之間
    if(i < length - k)
    {
        Merge(SR, TR, i, i+k-1, length-1);
    }
    //1組數據如下或者間隔超過了數組長度
    else
    {
        for(int j = i ; j < length ;j++)
        {
            TR[j] = SR[j];
        }
    }
    print(TR,length);
}
/*
 * 歸併排序(非遞歸)
 */
void MergeSort(int *array , int length)
{
    //開闢一個和數據同樣大的空間
    int *TR = (int *)malloc(sizeof(int) * length);
    //初始合併間隔爲1
    int k = 1;
    //k < length就不斷地合併
    while (k < length) {
        //把array合併到TR
        MergePass(array,TR,k,length);
        //間隔*2
        k *= 2;
        //把TR合併回來
        MergePass(TR, array, k, length);
        //間隔*2
        k *= 2;
    }
}

 

7.快速排序

快速排序是冒泡排序的一個升級,經過不斷調整元素位置來排序,是排序算法中最爲高效的

int Partition(int *array , int low , int high)
{
    //取第一個元素做爲目標位置
    int pivotKey = array[low];
    while (low < high) {
        //若是high比目標小,交換
        while (low < high && array[high] > pivotKey) {
            high--;
        }
        swip(&array[low], &array[high]);
        //若是low比目標大,交換
        while (low < high && array[low] < pivotKey) {
            low++;
        }
        swip(&array[low], &array[high]);
    }
    return low;
}

void Qsort(int *array , int low, int high)
{
    //目標值的位置
    int pivot;
    if(low < high)
    {
        //返回目標值位置
        pivot = Partition(array ,low ,high);
        //對前面進行快速排序
        Qsort(array, low, pivot-1);
        //對後面進行快速排序
        Qsort(array, pivot+1, high);
    }
}

void QuickSort(int *array , int length)
{
    Qsort(array,0,length-1);
}

 

總結

插入排序類 直接插入排序 希爾排序
選擇排序類 簡單選擇排序 堆排序
交換排序類 冒泡排序 快速排序
歸併排序類 歸併排序  

 

 

 

 

排序算法指標

排序方法 平均狀況 最好狀況 最壞狀況 輔助空間 穩定性
冒泡排序 O(n2) O(n) O(n2) O(1) 穩定
簡單選擇排序 O(n2) O(n2) O(n2) O(1) 穩定
直接插入排序 O(n2) O(n2) O(n2) O(1) 穩定
希爾排序 O(nlogn)-o(n2) O(n1.3) O(n2) O(1) 不穩定
堆排序 O(nlogn) O(nlogn) O(nlogn) O(1) 不穩定
歸併排序 O(nlogn) O(nlogn) O(nlogn) O(n) 穩定
快速排序 O(nlogn) O(nllogn) O(n2) O(logn)~O(n) 不穩定
相關文章
相關標籤/搜索