一、冒泡排序
原理
通过相邻元素的比较和交换,将最大元素逐步"冒泡"到数组末尾。每轮遍历确定一个最大值的最终位置,时间复杂度为 O(n²)。
优化点:若某轮未发生交换,说明数组已有序,可提前终止排序。
C 语言实现
#include <stdio.h>
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n-1; i++) { // 外层控制轮数
int swapped = 0;
for (int j = 0; j < n-i-1; j++) { // 内层比较相邻元素
if (arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
swapped = 1;
}
}
if (!swapped) break; // 提前终止优化
}
}
int main() {
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr)/sizeof(arr[0](@ref));
bubbleSort(arr, n);
printf("Sorted array: ");
for (int i=0; i<n; i++) printf("%d ", arr[i]);
return 0;
}
二、选择排序
原理
每次从未排序部分选择最小元素,与未排序部分的起始元素交换位置。时间复杂度始终为 O(n²),空间复杂度 O(1)。
C 语言实现
#include <stdio.h>
void selectionSort(int arr[], int n) {
for (int i = 0; i < n-1; i++) {
int minIndex = i;
for (int j = i+1; j < n; j++) { // 寻找最小值索引
if (arr[j] < arr[minIndex]) minIndex = j;
}
int temp = arr[i]; // 交换元素
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
int main() {
int arr[] = {64, 25, 12, 22, 11};
int n = sizeof(arr)/sizeof(arr[0](@ref));
selectionSort(arr, n);
printf("Sorted array: ");
for (int i=0; i<n; i++) printf("%d ", arr[i]);
return 0;
}
三、归并排序
原理
采用分治策略:将数组递归拆分为左右子数组,直到子数组长度为 1,再合并两个有序子数组。时间复杂度稳定为 O(n log n),但需要 O(n) 额外空间。
C 语言实现
#include <stdio.h>
#include <stdlib.h>
void merge(int arr[], int l, int m, int r) {
int n1 = m - l + 1, n2 = r - m;
int *L = (int*)malloc(n1 * sizeof(int));
int *R = (int*)malloc(n2 * sizeof(int));
for (int i = 0; i < n1; i++) L[i] = arr[l + i];
for (int j = 0; j < n2; j++) R[j] = arr[m + 1 + j];
int i = 0, j = 0, k = l;
while (i < n1 && j < n2) { // 合并有序数组
arr[k++] = (L[i] <= R[j]) ? L[i++] : R[j++];
}
while (i < n1) arr[k++] = L[i++];
while (j < n2) arr[k++] = R[j++];
free(L); free(R);
}
void mergeSort(int arr[], int l, int r) {
if (l >= r) return;
int m = l + (r - l)/2;
mergeSort(arr, l, m); // 递归拆分左半
mergeSort(arr, m+1, r); // 递归拆分右半
merge(arr, l, m, r); // 合并左右部分
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7};
int n = sizeof(arr)/sizeof(arr[0](@ref));
mergeSort(arr, 0, n-1);
printf("Sorted array: ");
for (int i=0; i<n; i++) printf("%d ", arr[i]);
return 0;
}
四、快速排序
原理
通过选取基准元素(如最后一个元素),将数组分为小于基准和大于基准的两部分,递归处理子数组。平均时间复杂度 O(n log n),最坏 O(n²)。
C 语言实现
#include <stdio.h>
void swap(int* a, int* b) {
int t = *a;
*a = *b;
*b = t;
}
int partition(int arr[], int low, int high) {
int pivot = arr[high]; // 选取最后一个元素为基准
int i = (low - 1);
for (int j = low; j <= high-1; j++) {
if (arr[j] < pivot) {
i++;
swap(&arr[i], &arr[j]); // 交换小于基准的元素到左侧
}
}
swap(&arr[i+1], &arr[high]); // 将基准放到正确位置
return (i + 1);
}
void quickSort(int arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi-1); // 递归左半部分
quickSort(arr, pi+1, high); // 递归右半部分
}
}
int main() {
int arr[] = {10, 7, 8, 9, 1, 5};
int n = sizeof(arr)/sizeof(arr[0](@ref));
quickSort(arr, 0, n-1);
printf("Sorted array: ");
for (int i=0; i<n; i++) printf("%d ", arr[i]);
return 0;
}
五、桶排序
原理
将元素分布到多个桶中,每个桶单独排序后合并。适用于均匀分布的数据,理想时间复杂度 O(n + k)。
C 语言实现
#include <stdio.h>
#include <stdlib.h>
#define BUCKET_SIZE 10 // 假设数据范围是0-99
typedef struct Node {
int data;
struct Node* next;
} Node;
void insert(Node** bucket, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = NULL;
if (*bucket == NULL) {
*bucket = newNode;
} else {
Node* curr = *bucket;
while (curr->next != NULL) curr = curr->next;
curr->next = newNode;
}
}
void bucketSort(int arr[], int n) {
Node** buckets = (Node**)malloc(BUCKET_SIZE * sizeof(Node*));
for (int i=0; i<BUCKET_SIZE; i++) buckets[i] = NULL;
// 将元素分配到桶中
for (int i=0; i<n; i++) {
int index = arr[i] / 10; // 假设元素范围0-99
insert(&buckets[index], arr[i]);
}
// 合并桶到原数组
int idx = 0;
for (int i=0; i<BUCKET_SIZE; i++) {
Node* curr = buckets[i];
while (curr != NULL) {
arr[idx++] = curr->data;
curr = curr->next;
}
}
// 释放内存(此处省略链表节点释放代码)
free(buckets);
}
int main() {
int arr[] = {29, 25, 3, 49, 9, 37, 21, 43};
int n = sizeof(arr)/sizeof(arr[0](@ref));
bucketSort(arr, n);
printf("Sorted array: ");
for (int i=0; i<n; i++) printf("%d ", arr[i]);
return 0;
}
总结与对比
算法 | 时间复杂度 | 稳定性 | 适用场景 |
---|---|---|---|
冒泡排序 | O(n²) | 稳定 | 小规模数据或接近有序数据 |
选择排序 | O(n²) | 不稳定 | 简单实现,内存有限制 |
归并排序 | O(n log n) | 稳定 | 大规模数据,需要稳定性 |
快速排序 | O(n log n) 平均 | 不稳定 | 通用场景,内存敏感 |
桶排序 | O(n + k)(理想情况) | 稳定 | 均匀分布数据,外部排序 |