算法学习笔记(2)——归并排序

归并排序

归并排序的思想是基于分治法,其思路是:

  • 将待排序区间平分成左右两半,左右两侧分别递归地做归并排序。
  • 将这两个有序的区间合并(每次落一个较小的下来),就将这个区间排好序了。

归并排序相比快速排序,几乎没有什么难理解的边界问题。要注意代码中的q[i] <= q[j]时从左半边落下元素,如果改成q[i] < q[j]也是能正确排序的,但是这样会丢失【归并排序是稳定的】这个特性。要保持这个特性,在排序字段相等的时候,应该落下来自左侧区间的元素,所以这里用<=

我们知道,归并排序的过程中,需要对当前区间进行对半划分,直到区间的长度为1。也就是说,每一层的子区间,长度都是上一层的1/2。这也就意味着,当划分到第 \(\log n\) 层的时候,子区间的长度就是1了。

而归并排序的merge操作,则是从最底层开始(子区间为1的层),对相邻的两个子区间进行合并,对于每一层来说,在合并所有子区间的过程中,\(n\) 个元素都会被操作一次,所以每一层的时间复杂度都是 \(O(n)\)。而之前我们说过,归并排序划分子区间,将子区间划分为只剩 \(1\) 个元素,需要划分 \(\log n\) 次。每一层的时间复杂度为 \(O(n)\),共有 \(\log n\) 层,所以归并排序的时间复杂度就是\(O(n\log n)\)

题目链接:AcWing 787. 归并排序

#include <iostream>

using namespace std;

const int N = 100010;

int n;
int q[N], tmp[N];

void merge_sort(int q[], int l, int r)
{
    if (l >= r) return;
    
    int mid = l + r >> 1;
    merge_sort(q, l, mid), merge_sort(q, mid + 1, r);
    
    int i = l, j = mid + 1, k = 0;
    while (i <= mid && j <= r) {
        if (q[i] < q[j]) tmp[k ++] = q[i ++];
        else             tmp[k ++] = q[j ++];
    }
    while (i <= mid) tmp[k ++] = q[i ++];
    while (j <= r)   tmp[k ++] = q[j ++];
    
    for (int i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> q[i];
    merge_sort(q, 0, n - 1);
    for (int i = 0; i < n; i ++ ) cout << q[i] << ' ';
    return 0;
}
posted @ 2022-12-09 21:49  S!no  阅读(49)  评论(0编辑  收藏  举报