归并排序核心思想是将数组分成两个子数组,分别对子数组进行排序,然后将排序后的子数组合并成一个有序的数组。归并排序的时间复杂度为 O(nlog⁡n),空间复杂度为 O(n),并且是一种稳定排序算法


1. 归并排序的基本思想

归并排序的核心思想是分治法。具体步骤如下:

  1. 分解:将待排序的数组分成两个子数组,分别对这两个子数组进行排序

  2. 递归:递归地对子数组进行排序,直到子数组的长度为1(此时子数组已经有序)

  3. 合并:将两个有序的子数组合并成一个有序的数组

归并排序的关键在于合并操作,即将两个有序数组合并成一个有序数组的过程


2. 合并操作

2.1 合并操作的实现

合并操作是归并排序的核心步骤。假设我们有两个已经排好序的数组 a 和 b,我们需要将它们合并成一个有序的数组 c

合并操作的步骤如下:

  1. 初始化三个指针 ijk,分别指向数组 abc 的起始位置

  2. 比较 a[i] 和 b[j],将较小的元素放入 c[k],并移动相应的指针

  3. 重复步骤2,直到其中一个数组的所有元素都被放入 c 中

  4. 将另一个数组中剩余的元素依次放入 c 中

以下是合并操作的代码实现:

void merge(int a[], int low, int mid, int hight) {
    int b[1000];  // 辅助数组
    int i = low, j = mid + 1, k = 0;  // i指向左半部分,j指向右半部分,k指向辅助数组
    while (i <= mid && j <= hight) {
        if (a[i] <= a[j])  // 将较小的元素放入辅助数组
            b[k++] = a[i++];
        else
            b[k++] = a[j++];
    }
    while (i <= mid)  // 将左半部分剩余的元素放入辅助数组
        b[k++] = a[i++];
    while (j <= hight)  // 将右半部分剩余的元素放入辅助数组
        b[k++] = a[j++];
    k = 0;
    for (int i = low; i <= hight; i++)  // 将辅助数组中的元素复制回原数组
        a[i] = b[k++];
}

2.2 合并操作的时间复杂度

合并操作的时间复杂度为 O(n),其中 n是两个子数组的总长度。因为我们需要遍历两个子数组的所有元素,并将它们合并到一个新的数组中


3. 递归分治

3.1 分治法的基本思想

分治法是一种算法设计思想,它将一个大问题分解成若干个相似的子问题,递归地解决这些子问题,然后将子问题的解合并成原问题的解。归并排序正是利用了分治法的思想。

3.2 归并排序的递归实现

归并排序的递归实现步骤如下:

  1. 分解:将数组从中间分成两个子数组。

  2. 递归:对这两个子数组分别进行归并排序。

  3. 合并:将排序后的两个子数组合并成一个有序的数组。

以下是归并排序的递归实现代码:

void mergesort(int* a, int low, int hight) {
    if (low < hight) {  // 递归终止条件
        int mid = (low + hight) / 2;
        mergesort(a, low, mid);       // 对左半部分进行归并排序
        mergesort(a, mid + 1, hight); // 对右半部分进行归并排序
        merge(a, low, mid, hight);    // 合并左右两部分
    }
}


4. 完整归并排序的实现

4.1 代码实现

以下是完整的归并排序代码实现,包括合并操作和递归分治:

#include<bits/stdc++.h>
using namespace std;

void merge(int a[], int low, int mid, int hight) {
    int b[1000];  // 辅助数组
    int i = low, j = mid + 1, k = 0;  // i指向左半部分,j指向右半部分,k指向辅助数组
    while (i <= mid && j <= hight) {
        if (a[i] <= a[j])  // 将较小的元素放入辅助数组
            b[k++] = a[i++];
        else
            b[k++] = a[j++];
    }
    while (i <= mid)  // 将左半部分剩余的元素放入辅助数组
        b[k++] = a[i++];
    while (j <= hight)  // 将右半部分剩余的元素放入辅助数组
        b[k++] = a[j++];
    k = 0;
    for (int i = low; i <= hight; i++)  // 将辅助数组中的元素复制回原数组
        a[i] = b[k++];
}

void mergesort(int* a, int low, int hight) {
    if (low < hight) {  // 递归终止条件
        int mid = (low + hight) / 2;
        mergesort(a, low, mid);       // 对左半部分进行归并排序
        mergesort(a, mid + 1, hight); // 对右半部分进行归并排序
        merge(a, low, mid, hight);    // 合并左右两部分
    }
}

int main() {
    int n, a[100];
    cin >> n;
    for (int i = 0; i < n; i++)
        cin >> a[i];
    mergesort(a, 0, n - 1);
    for (int i = 0; i < n; i++)
        cout << a[i] << " ";
    return 0;
}

4.2 时间复杂度分析

归并排序的时间复杂度为 O(nlog⁡n)。具体分析如下:

  • 分解:每次将数组分成两半,分解的次数为 log⁡n

  • 合并:每次合并操作的时间复杂度为 O(n)

  • 因此,总的时间复杂度为 O(nlog⁡n)

4.3 空间复杂度分析

归并排序的空间复杂度为 O(n),主要是因为需要一个辅助数组来存储合并后的结果


5. 归并排序的稳定性

归并排序是一种稳定排序算法。稳定性指的是在排序过程中,相等的元素的相对位置不会改变。归并排序在合并操作中,当两个元素相等时,优先选择左边的元素,因此保证了排序的稳定性


6. 归并排序的优缺点

优点:

  • 时间复杂度为 O(nlog⁡n),适合处理大规模数据

  • 是一种稳定排序算法

缺点:

  • 空间复杂度为 O(n),需要额外的存储空间

  • 对于小规模数据,归并排序的性能可能不如插入排序等简单排序算法

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐