經典排序算法的部分實現(C語言)

經典排序部分算法實現(C語言)

表格描述

中文名稱 英文名稱 平均時間複雜度 最壞時間複雜度 最好時間複雜度 空間複雜度 穩定性
選擇排序 Selection n2 n2 n2 1 不穩
冒泡排序 Bubble n2 n2 n 1
插入排序 Insertion n2 n2 n 1
堆排序 Heap nlog2n nlog2n nlog2n 1 不穩
希爾排序 Shell n1.3 n2 n 1 不穩
歸併排序 Merge nlog2n nlog2n nlog2n n
快速排序 Quick nlog2n n2 nlog2n nlog2n 不穩
桶排序 Bucket n + k n2 n n + k
計數排序 Counting n + k n + k n + k n + k
基數排序 Radix n * k n * k n * k n + k

代碼部分實現

選擇排序

選擇排序就是找到最大 / 最小的元素放在最後, 然後一個一個堆在數組一端最終完成排序, 代碼如下

#include <stdio.h> 

void swap(int arr[], int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

int find_max_index(int arr[], int n) {
    int max_pos = 0;
    int max = arr[0];
    
    for (int i = 0; i < n; i++) {
        if (arr[i] > max) {
			max = arr[i];
            max_pos = i;
        }
    }
    return max_pos;
}

void selection_sort(int arr, int n) {
    while (n > 1) {
        int max_pos = find_max_index(arr, n);
        swap(arr, max_pos, n - 1);
        n--;
    }
}

int main() {
    int arr[] = {1, 3, 2, 4, 0, 1, 2};
    int n = sizeof(arr) / sizeof(arr[0]);
    selection_sort(arr, n);
    for (int i = 0; i < n; i++) {
        printf("%d\n", arr[i]);
    }
    return 0;
}

冒泡排序

冒泡排序和選擇排序類似, 從第一個元素開始, 每走一趟, 將一個最大 / 最小的元素放在數組的一端, 成爲"一趟" 排序, 當完成n - 1 趟後, 排序就完成了, 每次比較的元素就像一個水中的氣泡飄到水面上一樣, 故稱爲冒泡排序, 算法實現如下

#include <stdio.h>

void swap(int arr[], int i, int j) {
    int temp = arr[i]; 
    arr[i] = arr[j];
    arr[j] = temp;
}

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

int main() {
    int arr[] = {1, 3, 2, 4, 0, 1, 2};
    int n = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, n);
    for (int i = 0; i < n; i++) {
        printf("%d\n", arr[i]);
    }
    return 0;
}

插入排序

插入排序就是從頭開始, 找到前方的每個元素, 然後將這個元素放在該放的位置, 將自己放在開始比自己小的元素之後, 遍歷完成之後就排好序了, 代碼實現如下

#include <stdio.h>

void insert(int arr[], int n) {
    int i = n;
    int key = arr[n];
    while (arr[i - 1] > key && i > 0) {
        arr[i] = arr[i - 1];
        i--;
    }

    arr[i] = key;
}

void insertion_sort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        insert(arr, i);
    }
}

int main() {
    int arr[] = {1, 3, 2, 4, 0, 1, 2};
    int n = 7; 
    insertion_sort(arr, n);
    for (int i = 0; i < n; i++) {
        printf("%d\n", arr[i]);
    }
    return 0;
}

堆排序

堆排序就是將一個堆(“完全二叉樹”), 每次將最小 / 最大的元素與最後一個葉子節點交換, 然後將最後一個葉子節點(最大 / 最小值)其砍斷, 放在數組中, 將剩餘的元素繼續構建成一個堆, 直到堆中的元素個數爲0, 代碼實現如下:

#include <stdio.h>

void heapify(int arr[], int i, int n) {
    if (i >= n) {
        return;
    }
    int c1 = 2 * i + 1;
    int c2 = 2 * i + 2;
    
    int max = i;
    
    // for c1 < n means that child must not out of range of this tree
    if (c1 < n && arr[c1] > arr[max]) {
        max = c1;
    }
    
    if (c2 < n && arr[c2] > arr[max]) {
        max = c2;
    }
    
    if (max != i) {
    	swap(arr, max, i);
        // continue to heapify, make it a big tree
        heapify(arr, max, n);
    }
}

void build_tree(int arr[], int n) {
    int last_node = n - 1;
    int parent = (last_node - 1) / 2;
    
    for (int i = parent; i >= 0; i--) {
        heapify(arr, i, n);
    }
}

void heap_sort(int arr[], int n) {
    build_tree(arr, n);
    for (int i = n - 1; i >= 0; i--) {
        swap(arr, 0, i);
        heapify(arr, 0, i);
    }
}

int main() {
    int arr[] = {1, 3, 2, 4, 0, 1, 2};
    int n = sizeof(arr) / sizeof(arr[0]);
    
    heap_sort(arr, n);
    for (int i = 0; i < n; i++) {
        printf("%d\n", arr[i]);
    }
    return 0;
}

歸併排序

歸併排序就是將一個數組進行不斷地拆分, 然後合併, 對數組進行拆分, 每次拆分成兩個子數組, 這兩個子數組, 每一個都是有序的, 如何保證呢? 當拆分後兩邊的數組只剩餘一個元素時, 那麼一定是有序的, 然後再進行比較歸併, 代碼實現如下

#include <stdio.h>

void merge(int arr, int r, int m, int l) {
    int left_size = m - r;
    int right_size = l - m + 1;
    
    int left[left_size];
    int right[right_size];
    
    int i, j, k;
    // fill left and right sub array
    for (i = l; i < m; i++) {
        left[i - l] = arr[i];
    }
    
    for (i = m; i <= r; i++) {
        right[i - m] = arr[i];
    }
    // i is index for left array, and j is for right here
    // k is index of complete array
    while (i < left_size && j < right_size) {
        if (left[i] < right[j]) {
        	arr[k++] = left[i++];    
        }
        else {
            arr[k++] = right[j++];
        }
    }
    
 	// if one of left and right arrays finish
    // and then the other one will fill the back
    while (i < left_size) {
        arr[k++] = left[i++];
    }
    
    while (j < right_size) {
        arr[k++] = right[j++];
    }
    
}

void merge_sort(int arr[], int l, int r) {
	// flag for recursion compeleting
    if (l == r){
        return;
    }
    int m = (r + l) / 2;
    merge_sort(arr, l, m);
    merge_sort(arr, m + 1, r);
    merge(arr, l, m, r);
}

int main() {
    int arr[] = {1, 3, 2, 4, 0, 1, 2};
    int n = sizeof(arr) / sizeof(arr[0]);
    merge_sort(arr, 0, n);
    for (int i = 0; i < n; i++) {
        printf("%d\n", arr[i]);
    }
    return 0;
}

快速排序

快速排序主要使用了分治法, 放兩個指針和一個哨兵, 一個快指針, 一個慢指針, 當快指針指向的元素小於哨兵時, 滿指針向前移, 然後與快指針的值進行交換, 一趟下來之後, 慢指針之後的元素都比哨兵大, 然後哨兵與慢指針位置的下一個進行交換, 做到了左邊的數字都比哨兵小, 右邊的數字都比哨兵大, 然後對每一遍進行快速排序, 直到排序完成, 代碼實現如下 :

#include <stdio.h>

void swap(int arr[], int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

int partition(int arr[], int low, int high) {
    int i = low - 1;
    int pivot = arr[high];
    
    for (int j = low; j < high; j++) {
        if (arr[j] < pivot) {
            i++;
            swap(arr, i, j);
        }
    }
    swap(arr, i + 1, high);
    return i + 1;
}

void quick_sort(int arr[], int low, int high) {
    if (low < high) {
        int p = partition(arr, low, high);
        quick_sort(arr, low, p - 1);
        quick_sort(arr, p + 1, high);
    }
}

int main() {
    int arr[] = {1, 3, 2, 4, 0, 1, 2};
    int n = sizeof(arr) / sizeof(arr[0]);
    
    quick_sort(arr, 0, n);
    
    for (int i = 0; i < high; i++) {
        printf("%d\n", arr[i]);
    }
    return 0;
}

總結

以上的各種排序算法, 實際上全是通過元素的比較與交換進行. 所以在進行排序的過程就是進行比較的過程

使用c語言的主要目的是, c語言比較簡單, 所有的語句都要自己寫, 更能使讀者方便.

以上使用的全部是最通俗的c語言, 沒有使用指針以及引用類型

以下是swap的引用類型寫法, 使得swap的函數可讀性更強.

void swap(int &a, int &b) {
	int temp = &a; 
	&a = &b;
	&b = temp;
}

如有錯誤, 歡迎評論區, 或者聯繫我進行改正,謝謝

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