分治:改进的归并排序(使用链接表)与例题详解

1.未改进的归并排序

#include <iostream>

void merge(int a[], int left, int mid, int right) {
    if (left >= right)
        return;
    int *t = new int[right - left + 1];
    int i = left, j = mid + 1, cnt = 0;
    while(true) {
        // 超出某部分范围说明其中一块已经用完了
        if (i == mid + 1 || j == right + 1)
            break;
        if (a[i] >= a[j]) {
            t[cnt++] = a[j];
            j++;
        } 
        else {
            t[cnt++] = a[i];
            i++;
        }
    }

    // 将剩下的补充到临时数组
    if (i == mid + 1) {
        for (int k = j; k <= right; ++k) {
            t[cnt++] = a[k];
        }
    }
    else {
        for (int k = i; k <= mid; ++k) {
            t[cnt++] = a[k];
        }
    }
    // 赋值给原数组
    for (int k = 0; k < right - left + 1; ++k) {
        a[left + k] = t[k];
    }
}

void mergeSort(int a[], int left, int right) {
    if (left < right) {
        int mid = (left + right) / 2;
        mergeSort(a, left, mid);
        mergeSort(a, mid + 1, right);
        merge(a, left, mid, right);
    }
}

int main() {
    int a[] = {2, 4, 22, 1, 78, 23, 90, 11, 73};
    mergeSort(a, 0, 8);

    for (int i = 0; i < 9; ++i) {
        std::cout << a[i] << " ";
    }
    return 0;
}

输出:

1 2 4 11 22 23 73 78 90

问题:频繁使用临时数组进行元素移动。

2.改进:不使用临时数组,使用链接表

#include <iostream>

int merge(int a[], int link[], int p, int q) {
    int i = p, j = q, k = 0;
    while (i != 0 && j != 0) {
        if (a[i] > a[j]) {  // 找较小的关键字,链接
            link[k] = j;
            k = j;
            j = link[j];
        }
        else {
            link[k] = i;
            k = i;
            i = link[i];
        }
    }
    
    if (i == 0) { // 较大者连接到小者的后面
        link[k] = j;
    }
    else {
        link[k] = i;
    }
    return link[0]; // 返回r表
}

int mergeSort(int a[], int link[], int left, int right) {
    if (left < right) {
        int mid = (left + right) / 2;
        int p = mergeSort(a, link, left, mid); // p表
        int q = mergeSort(a, link, mid + 1, right); // q表
        // 返回r表
        return merge(a, link, p, q);
    }
    // 单个节点的r表即此节点的下标
    return left;
}

int main() {

    int a[] = {2, 4, 22, 1, 78, 23, 90, 11, 73};

    int *link = new int[9];

    for (int i = 0; i < 9; ++i) {
        link[i] = 0;
    }

    mergeSort(a, link, 1, 8);

    int k = link[0];
    std::cout << a[k] << " ";
    while (link[k] != 0) {
        std::cout << a[link[k]] << " ";
        k = link[k];
    }

    return 0;
}

输出:

1 4 11 22 23 73 78 90

3.链接过程

在这里插入图片描述
在这里插入图片描述
第15次递归时,合并了所有数组。最终的从小到大的顺序下标为:

link[0] = 2 (最小下标)
link[2] = 5
link[5] = 3
link[3] = 4
link[4] = 7
link[7] = 1
link[1] = 8
link[8] = 6
link[6] = 0 (不包括)
  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伯明翰谢老二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值