C語言實現九大排序演算法
直接插入排序
將陣列分為兩個部分,一個是有序部分,一個是無序部分。從無序部分中依次取出元素插入到有序部分中。過程就是遍歷有序部分,實現起來比較簡單。
#include <stdio.h>
void insertion_sort(int arr[], int array_length) {
for (int i = 0; i < array_length; ++i) {
int data = arr[i];
int j = 0;
while (arr[j] < arr[i]) {
j++;
}
for (int k = i; k >= j + 1; k--) {
arr[k] = arr[k - 1];
}
arr[j] = data;
}
}
void print_array(int arr[], int array_length) {
for (int i = 0; i < array_length; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[7] = {8, 2, 6, 0, 5, 7, 4};
insertion_sort(arr, 7);
print_array(arr, 7);
return 0;
}
折半插入排序
折半插入再直接插入上有改進,用折半搜尋替換遍歷陣列,在陣列長度大時能夠提升查詢效能。其本質還是從無序部分取出元素插入到有序部分中。
#include <stdio.h>
void binary_insertion_sort(int arr[], int array_length) {
int i, j, low = 0, high = 0, mid;
int temp = 0;
for (i = 1; i < array_length; i++) {
low = 0;
high = i - 1;
temp = arr[i];
while (low <= high) {
mid = (low + high) / 2;
if (arr[mid] > temp) {
high = mid - 1;
} else {
low = mid + 1;
}
}
for (j = i; j > low; j--) {
arr[j] = arr[j - 1];
}
arr[low] = temp;
}
}
void print_array(int arr[], int array_length) {
for (int i = 0; i < array_length; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int brr[5] = {2, 6, 0, 5, 7};
binary_insertion_sort(brr, 5);
print_array(brr, 5);
return 0;
}
希爾排序
希爾排序的核心就是根據步長分組,組內進行插入排序。關於步長的選取,第一次步長取元素的個數,後面每次取原來步長的一半。
希爾排序屬於插入排序的一種。
#include <stdio.h>
void shell_sort(int arr[], int array_length) {
int step = array_length / 2;
while (step >= 1) {
for (int i = 0; i < array_length; i += step) {
int data = arr[i];
int j = 0;
while (arr[j] < arr[i]) {
j++;
}
for (int k = i; k >= j + 1; k--) {
arr[k] = arr[k - 1];
}
arr[j] = data;
}
step = step / 2;
}
}
void print_array(int arr[], int array_length) {
for (int i = 0; i < array_length; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int crr[10] = {73, 22, 93, 43, 55, 14, 28, 65, 39, 81};
shell_sort(crr, 10);
print_array(crr, 10);
return 0;
}
氣泡排序
冒泡的特點是兩兩交換。通過交換把最大的元素交換到後面去了,每次迴圈遍歷都把無序部分最大的“沉”到後面去。小數上“浮”和大數下“沉”其實沒有差別,都能實現冒泡。
#include <stdio.h>
void bubble_sort(int arr[], int array_length) {
for (int i = 0; i < array_length - 1; ++i) {
for (int j = 0; j < array_length - i - 1; ++j) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
void print_array(int arr[], int array_length) {
for (int i = 0; i < array_length; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int drr[7] = {8, 2, 6, 0, 5, 7, 4};
bubble_sort(drr, 7);
print_array(drr, 7);
return 0;
}
快速排序
快排的精髓在於選定一個標準(通常選陣列的第一個元素),然後將所有元素根據標準分為小於和大於兩個部分,然後這兩個部分再選取標準,繼續遞迴下去,不難想象最終排序結果是整體有序的。
#include <stdio.h>
int getStandard(int arr[], int low, int high) {
int flag = arr[low];
while (low < high) {
while (low < high && arr[high] >= flag) {
high--;
}
if (low < high) {
arr[low] = arr[high];
}
while (low < high && arr[low] <= flag) {
low++;
}
if (low < high) {
arr[high] = arr[low];
}
}
arr[low] = flag;
return low;
}
void quick_sort(int arr[], int low, int high) {
if (low < high) {
int pos = getStandard(arr, low, high);
quick_sort(arr, low, pos - 1);
quick_sort(arr, pos + 1, high);
}
}
void print_array(int arr[], int array_length) {
for (int i = 0; i < array_length; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int err[10] = {73, 22, 93, 43, 55, 14, 28, 65, 39, 81};
quick_sort(err, 0, 9);
print_array(err, 10);
return 0;
}
直接選擇排序
如其名,直接選擇一個最小的放到最前面,但是遍歷往往導致效率較低。
#include <stdio.h>
void select_sort(int arr[], int array_length) {
for (int i = 0; i < array_length; ++i) {
int min_pos = i;
for (int j = i; j < array_length; ++j) {
if (arr[min_pos] > arr[j])
min_pos = j;
}
int temp = arr[min_pos];
arr[min_pos] = arr[i];
arr[i] = temp;
}
}
void print_array(int arr[], int array_length) {
for (int i = 0; i < array_length; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int frr[7] = {8, 2, 6, 0, 5, 7, 4};
select_sort(frr, 7);
print_array(frr, 7);
return 0;
}
堆排序
將陣列轉換為一顆完全二叉樹。任意一個父節點大於它的子節點,這樣的完全二叉樹叫做大頂堆;與之相反的,任意一個父節點小於它的子節點,這樣的完全二叉樹叫做小頂堆。
堆排序的精華就在於把元素個數為n的完全二叉樹轉換為大頂堆,然後把堆頂和最後一個元素交換,此時產生了一個元素個數為n-1的完全二叉樹,然後再轉換為大頂堆,繼續把堆頂和最後一個元素交換。迴圈往復就實現了排序。其實質還是選擇排序,每次選出一個最大的,和最後一個交換,不過完全二叉樹中選最大元素比遍歷陣列會快很多。
#include <stdio.h>
void heap_adjust(int arr[], int n) {
for (int i = n / 2; i >= 1; i--) {
if (arr[i - 1] < arr[2 * i - 1]) {
int temp = arr[i - 1];
arr[i - 1] = arr[2 * i - 1];
arr[2 * i - 1] = temp;
}
if (arr[i - 1] < arr[2 * i] && (2 * i) < n) {
int temp = arr[i - 1];
arr[i - 1] = arr[2 * i];
arr[2 * i] = temp;
}
}
}
void heap_sort(int arr[], int array_length) {
int n = array_length;
do {
heap_adjust(arr, n);
int temp = arr[0];
arr[0] = arr[n - 1];
arr[n - 1] = temp;
} while (n--);
}
void print_array(int arr[], int array_length) {
for (int i = 0; i < array_length; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int grr[7] = {8, 2, 6, 0, 5, 7, 4};
heap_sort(grr, 7);
print_array(grr, 7);
return 0;
}
歸併排序
歸併的思想在於對複雜問題的分治,打散到最小長度後然後再進行合併操作。假設有兩個陣列A、B,指標i指向A的頭部,指標j指向B的頭部,兩邊同時進行遍歷,找到一個小的就放到陣列裡面,對應指標後移一位,這樣就能夠保證合併後的陣列是有序的。
#include <stdio.h>
#include <malloc.h>
void merge(int arr[], int start, int mid, int end) {
int *new_array = (int *) malloc(sizeof(int) * (end - start + 1));
int i = start;
int j = mid + 1;
int k = 0;
while (i <= mid && j <= end) {
if (arr[i] < arr[j]) {
new_array[k++] = arr[i++];
} else {
new_array[k++] = arr[j++];
}
}
while (i <= mid) {
new_array[k++] = arr[i++];
}
while (j <= end) {
new_array[k++] = arr[j++];
}
for (int l = 0; l < k; ++l) {
arr[start + l] = new_array[l];
}
free(new_array);
}
void merge_sort(int arr[], int start, int end) {
int mid = (start + end) / 2;
if (start >= end) {
return;
}
merge_sort(arr, start, mid);
merge_sort(arr, mid + 1, end);
merge(arr, start, mid, end);
}
void print_array(int arr[], int array_length) {
for (int i = 0; i < array_length; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int hrr[10] = {73, 22, 93, 43, 55, 14, 28, 65, 39, 81};
merge_sort(hrr, 0, 9);
print_array(hrr, 10);
return 0;
}
基數排序
先按照個位排序將所有數字分配到0-9這10個桶裡面,然後再按照桶的順序收集起來;再按照十位排序,同樣的步驟……
基礎排序的本質是對每一位進行排序,對每一位進行排序後就能保證這一個數整體的大小是按照順序排列的。
#include <stdio.h>
#include <malloc.h>
int get_num(int number, int pos) {
int num = 0;
while (pos--) {
num = number % 10;
number = number / 10;
}
return num;
}
void radix_sort(int arr[], int array_length) {
int *bucket[10];
for (int i = 0; i < 10; ++i) {
bucket[i] = (int *) malloc(sizeof(int) * array_length + 1);
bucket[i][0] = 0;//桶的第一位儲存桶中元素個數
}
for (int b = 1; b <= 31; ++b) {
for (int i = 0; i < array_length; ++i) {
int num = get_num(arr[i], b);//計算每個位上的數字(個位、十位、百位...)
int index = ++bucket[num][0];//計算下標
bucket[num][index] = arr[i];//儲存到桶中
}
for (int i = 0, k = 0; i < 10; i++) {
for (int j = 1; j <= bucket[i][0]; ++j) {
arr[k++] = bucket[i][j];//從桶裡面按順序取出來
}
bucket[i][0] = 0;//下標清零
}
}
}
void print_array(int arr[], int array_length) {
for (int i = 0; i < array_length; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int irr[10] = {73, 22, 93, 43, 55, 14, 28, 65, 39, 81};
radix_sort(irr, 10);
print_array(irr, 10);
return 0;
}