1.1 归并排序
归并排序(Merge Sort)是一种基于分治法(Divide and Conquer)的高效排序算法。它将数组分成两个子数组,分别对这两个子数组进行排序,然后将它们合并成一个有序数组。
算法步骤
1. 分解:将待排序数组分成两个子数组,递归地对这两个子数组进行排序。
2. 合并:将两个已排序的子数组合并成一个有序数组。
// 合并两个有序数组的函数
void merge(int a[], int left, int mid, int right) {
// 计算左子数组的长度
int n1 = mid - left + 1;
// 计算右子数组的长度
int n2 = right - mid;
// 创建临时数组 L 存储左子数组
int L[n1];
// 创建临时数组 R 存储右子数组
int R[n2];
// 将左子数组的元素复制到 L 中
for (int i = 0; i < n1; ++i)
L[i] = a[left + i];
// 将右子数组的元素复制到 R 中
for (int j = 0; j < n2; ++j)
R[j] = a[mid + 1 + j];
// 初始化三个指针:i 用于遍历左子数组,j 用于遍历右子数组,k 用于合并后的数组
int i = 0, j = 0, k = left;
// 当左子数组和右子数组都还有元素时
while (i < n1 && j < n2) {
// 如果左子数组的当前元素小于等于右子数组的当前元素
if (L[i] <= R[j]) {
// 将左子数组的当前元素放入合并后的数组
a[k] = L[i];
// 左子数组指针向前移动
++i;
} else {
// 将右子数组的当前元素放入合并后的数组
a[k] = R[j];
// 右子数组指针向前移动
++j;
}
// 合并后数组的指针向前移动
++k;
}
// 如果左子数组还有剩余元素,将其放入合并后的数组
while (i < n1) {
a[k] = L[i];
++i;
++k;
}
// 如果右子数组还有剩余元素,将其放入合并后的数组
while (j < n2) {
a[k] = R[j];
++j;
++k;
}
}
// 归并排序的主函数
void mergeSort(int a[], int left, int right) {
// 如果左边界小于右边界,即子数组至少有两个元素
if (left < right) {
// 计算中间位置
int mid = left + (right - left) / 2;
// 对左子数组进行归并排序
mergeSort(a, left, mid);
// 对右子数组进行归并排序
mergeSort(a, mid + 1, right);
// 合并左右子数组
merge(a, left, mid, right);
}
}
merge(int a[], int left, int mid, int right):
合并两个有序数组 L 和 R,并将结果存放在数组 a 中。
n1 和 n2 分别是两个子数组的长度。
将两个子数组中的元素按顺序合并到数组 a 中。
mergeSort(int a[], int left, int right):
递归地将数组分成两个子数组,并对它们进行排序。
mid 是数组的中间位置。
递归调用 mergeSort 对左右子数组进行排序,然后调用 merge 函数合并它们。
时间复杂度
·最优时间复杂度:O(nlogn)
·最坏时间复杂度:O(nlogn)
·平均时间复杂度:O(nlogn)
空间复杂度
·空间复杂度:O(n)(需要额外的数组空间来存储合并结果)
稳定性
归并排序是一种稳定的排序算法,因为相同元素的相对顺序不会改变。
1.2 基数排序
基数排序(Radix Sort)是一种非比较型的排序算法,适用于整数和字符串等数据类型。它通过将数据按位数进行处理,从最低位到最高位(或相反)逐位排序,从而实现整体排序。基数排序有两种主要方法:LSD(Least Significant Digit first)和 MSD(Most Significant Digit first)12。
LSD 基数排序
LSD 基数排序从最低有效位开始排序,逐位向高位进行。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 计数排序函数,用于对指定基数位进行排序
void countingSort(vector<int>& arr, int exp) {
int n = arr.size(); // 获取数组的大小
vector<int> output(n); // 创建一个与输入数组大小相同的输出数组
int count[10] = {0}; // 用于计数的数组,初始化为 0
// 统计每个基数位的出现次数
for (int i = 0; i < n; i++)
count[(arr[i] / exp) % 10]++;
// 计算累计计数
for (int i = 1; i < 10; i++)
count[i] += count[i - 1];
// 构建输出数组
for (int i = n - 1; i >= 0; i--) {
output[count[(arr[i] / exp) % 10] - 1] = arr[i];
count[(arr[i] / exp) % 10]--;
}
// 将排序结果复制回原始数组
for (int i = 0; i < n; i++)
arr[i] = output[i];
}
// 基数排序主函数
void radixSort(vector<int>& arr) {
int maxVal = *max_element(arr.begin(), arr.end()); // 找出数组中的最大值
// 对每个基数位进行计数排序
for (int exp = 1; maxVal / exp > 0; exp *= 10)
countingSort(arr, exp);
}
int main() {
int n;
cout << "请输入数组的大小: ";
cin >> n;
vector<int> arr(n);
cout << "请输入数组的元素: ";
for (int i = 0; i < n; ++i) {
cin >> arr[i];
}
radixSort(arr);
cout << "排序后的数组: ";
for (int i = 0; i < n; ++i) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
MSD 基数排序
MSD 基数排序从最高有效位开始排序,逐位向低位进行。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 最高有效位基数排序的辅助函数
void MSD_radix_sort(vector<int>& arr, int left, int right, int exp) {
// 如果左边界大于等于右边界或者当前基数位为 0,则返回
if (left >= right || exp == 0) return;
// 创建 10 个桶,用于存储不同基数位的数字
vector<vector<int>> buckets(10);
// 将数字分配到对应的桶中
for (int i = left; i <= right; ++i) {
int digit = (arr[i] / exp) % 10;
buckets[digit].push_back(arr[i]);
}
int index = left;
// 将桶中的数字放回原数组
for (int i = 0; i < 10; ++i) {
for (int num : buckets[i]) {
arr[index++] = num;
}
// 如果桶不为空,对该桶对应的数字进行递归排序
if (!buckets[i].empty()) {
MSD_radix_sort(arr, index - buckets[i].size(), index - 1, exp / 10);
}
}
}
// 基数排序的主函数
void radixSort(vector<int>& arr) {
int maxVal = *max_element(arr.begin(), arr.end()); // 找出数组中的最大值
int exp = 1;
// 计算最高有效位对应的基数
while (maxVal / exp > 0) exp *= 10;
// 调用辅助函数进行排序
MSD_radix_sort(arr, 0, arr.size() - 1, exp / 10);
}
int main() {
int n;
cout << "请输入数组的大小: ";
cin >> n;
vector<int> arr(n);
cout << "请输入数组的元素: ";
for (int i = 0; i < n; ++i) {
cin >> arr[i];
}
radixSort(arr);
cout << "排序后的数组: ";
for (int i = 0; i < n; ++i) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
时间复杂度
·最优时间复杂度:O(n⋅k),其中 k是数字的位数。
·最坏时间复杂度:O(n⋅k)
·平均时间复杂度:O(n⋅k)
空间复杂度
·空间复杂度:O(n+k)
稳定性
基数排序是一种稳定的排序算法,因为相同元素的相对顺序不会改变。