数据结构-归并类排序

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)

稳定性

基数排序是一种稳定的排序算法,因为相同元素的相对顺序不会改变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值