七種常見的數組排序算法整理(C語言版本)

~~~C語言版本~~~算法

  • 冒泡排序
  • 選擇排序
  • 直接插入排序
  • 二分插入排序
  • 希爾排序
  • 快速排序
  • 堆排序
#define EXCHANGE(num1, num2) { num1 = num1 ^ num2;\
num2 = num1 ^ num2;\
num1 = num1 ^ num2;}
複製代碼

排序算法是否穩定:相同元素的相對在排序先後是否會發生改變,若是會,就是不穩定的,不然就是穩定的。
一.冒泡排序
冒泡排序原理很容易理解,就是重複地走訪過要排序的元素列,依次比較兩個相鄰的元素,順序不對就交換,直至沒有相鄰元素須要交換,也就是排序完成。
這個算法的名字由來是由於越大的元素會經由交換慢慢「浮」到數列的頂端(升序或降序排列),就如同碳酸飲料中二氧化碳的氣泡最終會上浮到頂端同樣,故名「冒泡排序」。shell

  • 冒泡排序是一種穩定排序算法。
  • 時間複雜度:最好狀況(初始狀況就是正序)下是o(n),平均狀況是o(n²)
void buddleSort(int num[],int count)
{
    for (int i = 0; i < count - 1; i++) {

        for (int j = 0; j < count - i - 1; j++) {

            if (num[j] > num[j + 1]) EXCHANGE(num[j], num[j + 1])
        }
    }
}
複製代碼

2、選擇排序
選擇排序(Selection sort)是一種簡單直觀的排序算法。它的工做原理是每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,而後,再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾。以此類推,直到所有待排序的數據元素排完。
選擇排序的交換操做介於 0 和 (n - 1) 次之間。選擇排序的比較操做爲 n (n - 1) / 2 次之間。選擇排序的賦值操做介於 0 和 3 (n - 1) 次之間。
比較次數O(n²),比較次數與關鍵字的初始狀態無關,總的比較次數N=(n-1)+(n-2)+...+1=n*(n-1)/2。交換次數O(n),最好狀況是,已經有序,交換0次;最壞狀況交換n-1次,逆序交換n/2次。交換次數比冒泡排序少多了,因爲交換所需CPU時間比比較所需的CPU時間多,n值較小時,選擇排序比冒泡排序快api

  • 選擇排序是不穩定的排序方法。
  • 時間複雜度:最好和平均狀況下都是O(n²)
void selectSort(int num[],int count)
{
    for (int i = 0; i < count; i++) {

        int min = i;

        for (int j = i; j < count; j++) {
            
            if (num[j] < num[min])  min = j;
        }

        if (i != min)   EXCHANGE(num[i], num[min]);//能夠看出,最多交換count - 1次
    }
}
複製代碼

3、直接插入排序
插入排序的基本操做就是將一個數據插入到已經排好序的有序數據中,從而獲得一個新的、個數加一的有序數據,算法適用於少許數據的排序,
插入排序的基本思想是:每步將一個待排序的記錄,按其關鍵碼值的大小插入前面已經排序的文件中適當位置上,直到所有插入完爲止bash

  • 直接插入排序是穩定的排序算法。
  • 時間複雜度:最好狀況(初始狀況就是正序)下是o(n),平均狀況是o(n²)
void insertSort2(int num[],int count)
{
    int i,j;
    
    for (i = 1; i < count; i++) {
        
        if (num[i] < num[i - 1]) {
            
            int temp = num[i];
            
            for (j = i; j > 0; j--) {
                
                if (num[j - 1] > temp) num[j] = num[j - 1];
                
                else break;
            }
            
            num[j] = temp;
        }
    }
}
複製代碼

4、二分插入排序數據結構

因爲在插入排序過程當中,待插入數據左邊的序列老是有序的,針對有序序列,就能夠用二分法去插入數據了,也就是二分插入排序法。適用於數據量比較大的狀況。
二分插入排序的算法思想:
算法的基本過程:
(1)計算 0 ~ i-1 的中間點,用 i 索引處的元素與中間值進行比較,若是 i 索引處的元素大,說明要插入的這個元素應該在中間值和剛加入i索引之間,反之,就是在剛開始的位置 到中間值的位置,這樣很簡單的完成了折半;
(2)在相應的半個範圍裏面找插入的位置時,不斷的用(1)步驟縮小範圍,不停的折半,範圍依次縮小爲 1/2 1/4 1/8 .......快速的肯定出第 i 個元素要插在什麼地方;
(3)肯定位置以後,將整個序列後移,並將元素插入到相應位置。函數

  • 二分插入排序是穩定的排序算法。
  • 時間複雜度:最好狀況(恰好插入位置爲二分位置)下是O(log₂n),平均狀況和最壞狀況是o(n²)
void insertSortBinary(int num[],int count)
{
    int i,j;
    
    for (i = 1; i < count; i++) {
        
        if (num[i] < num[i - 1]) {
            
            int temp = num[i];
            
            int left = 0,right = i - 1;
            
            while (left <= right) {
                
                int mid = (left + right)/2;
                
                if (num[mid] < temp) left = mid + 1;
                    
                else right = mid - 1;
            }
            //只是比較次數變少了,交換次數仍是同樣的
            for (j = i; j > left; j--) {
                
                num[j] = num[j - 1];
            }
            
            num[left] = temp;
        }
    }
}
複製代碼

5、希爾(插入)排序
希爾排序(Shell's Sort)是插入排序的一種又稱「縮小增量排序」(Diminishing Increment Sort),是直接插入排序算法的一種更高效的改進版本。
希爾排序是把記錄按下標的必定增量分組,對每組使用直接插入排序算法排序;隨着增量逐漸減小,每組包含的關鍵詞愈來愈多,當增量減至1時,整個文件恰被分紅一組,排序完成。ui

  • 希爾排序是非穩定排序算法。
  • 時間複雜度:O(n^(1.3—2))
void shellSort(int num[],int count)
{
    int shellNum = 2;
    int gap = round(count/shellNum);

    while (gap > 0) {
        for (int i = gap; i < count; i++) {
            int temp = num[i];
            int j = i;
            while (j >= gap && num[j - gap] > temp) {
                num[j] = num[j - gap];
                j = j - gap;
            }
            num[j] = temp;
        }
        gap = round(gap/shellNum);
    }
}
複製代碼

6、快速排序
快速排序(Quicksort)是對冒泡排序的一種改進。spa

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

  • 快速排序是非穩定的排序算法
  • 時間複雜度:最差爲O(n^2),平均爲O(nlogn),最好爲O(nlogn)
void quickSort(int num[],int count,int left,int right)
{
    if (left >= right){
        
        return ;
    }
    int key = num[left];
    int lp = left;           //左指針
    int rp = right;          //右指針
    while (lp < rp) {
        if (num[rp] < key) {
            int temp = num[rp];
            for (int i = rp - 1; i >= lp; i--) {
                num[i + 1] = num[i];
            }
            num[lp] = temp;
            lp ++;
            rp ++;
        }
        rp --;
    }
    quickSort(num,count,left,lp - 1);
    quickSort(num,count,rp + 1,right);
}
複製代碼

7、堆排序
是指利用堆這種數據結構所設計的一種排序算法。堆是一個近似徹底二叉樹的結構,並同時知足堆積的性質:即子結點的鍵值或索引老是小於(或者大於)它的父節點指針

在堆的數據結構中,堆中的最大值老是位於根節點(在優先隊列中使用堆的話堆中的最小值位於根節點)。堆中定義如下幾種操做:

  • 最大堆調整(Max Heapify):將堆的末端子節點做調整,使得子節點永遠小於父節點

  • 建立最大堆(Build Max Heap):將堆中的全部數據從新排序

  • 堆排序(HeapSort):移除位在第一個數據的根節點,並作最大堆調整的遞歸運算

  • 堆排序是一個非穩定的排序算法。
  • 時間複雜度:O(nlogn)
void maxHeapify(int num[], int start, int end) {
    //創建父節點指標和子節點指標
    int dad = start;
    int son = dad * 2 + 1;
    while (son <= end) { //若子節點指標在範圍內才作比較
        if (son + 1 <= end && num[son] < num[son + 1]) //先比較兩個子節點大小,選擇最大的
            son++;
        if (num[dad] > num[son]) //若是父節點大於子節點表明調整完畢,直接跳出函數
            return;
        else { //不然交換父子內容再繼續子節點和孫節點比較
            EXCHANGE(num[dad], num[son])
            dad = son;
            son = dad * 2 + 1;
        }
    }
}

void heapSort(int num[], int count) {
    int i;
    //初始化,i從最後一個父節點開始調整
    for (i = count / 2 - 1; i >= 0; i--)
        maxHeapify(num, i, count - 1);
    //先將第一個元素和已排好元素前一位作交換,再從新調整,直到排序完畢
    for (i = count - 1; i > 0; i--) {
        EXCHANGE(num[0], num[i])
        maxHeapify(num, 0, i - 1);
    }
}
複製代碼
相關文章
相關標籤/搜索