归并排序C语言——递归版本

8 篇文章 0 订阅
6 篇文章 0 订阅

归并排序 从归并两个字眼可以感觉出 里面有递归

确实有递归 从标题也可以看出来 对于递归如果理解不是太好 就只能在纸上画图 这也是没有办法的办法 总不能不解决这个问题吧 我也是这样过来的 希望你能坚持下去 看着下面的图痛苦下去吧

这个图对递归的理解还是很重要的

从上图可以看出 归并排序 是将原数组通过不断分解 直到拆分为单个元素为止

再对单个元素进行两两比较 然后按照顺序考回数组中(不理解先看上面的图)

然后再对数组大小为二的数组进行合并 以此类推 直到合并为原数组 就有序了

这个排序理解起来确实不是很好理解 毕竟是和递归扯上关系的 上面也说了你应该怎样去理解他

不然你之后遇到递归就只能绕道而行

咱们先不管什么递归不递归 (递归不就是分 不断地分直到不能分为止)

先考虑 分完之后如何合并两个数组

void _MergeSort(int* a, int left, int right, int* tmp)
{

    int mid = (left + right) / 2;
    // [left, mid] [mid+1, right] 有序

    //就这样先将原数组分为两个子数组
    int begin1 = left, end1 = mid;//[left, mid]
    int begin2 = mid+1, end2 = right;//[mid+1, right]
    int i = left;//定位两个区间段比较之后在tmp数组中的开头位置
    //上文说过 你是将分完的数组排序后再考回原数组 
    //根据上面的图 下半部分 处理时 
    //笨点的办法就是 自己排一个区间段 自己再开辟一个对应大小数组 不断合并不断开辟 最后将开辟的数组 考回原数组
    //太麻烦了!!!
    //那就直接干脆 搞一个和原数组大小相同的数组tmp 
    //将排序之后的数组放到tmp对应的区间段 

    //你既然要考回tmp数组 那就要有个位置 不然就乱套了 
    //将原本数组该区间段的元素进行排序 (区间段先小后大) 
    while (begin1 <= end1 && begin2 <= end2)
    {
        if (a[begin1] < a[begin2])
        {
            tmp[i++] = a[begin1++];
        }
        else
        {
            tmp[i++] = a[begin2++];
        }
    }
    
    //经过上面的循环并不是所有的数都考回到了原数组 肯定某个区间段没拷完 既然不知道那就写两个循环进行拷贝 不管谁拷完谁没拷完
    while (begin1 <= end1)
    {
        tmp[i++] = a[begin1++];
    }

    while (begin2 <= end2)
    {
        tmp[i++] = a[begin2++];
    }

    // tmp 数组拷贝回a
    for (int j = left; j <= right; ++j)
    {
        a[j] = tmp[j];
    }
}

再啰嗦一下上面的实现思路

将数组层层拆分 拆成单个的数字 再将他们比较排序放入tmp数组中 对应的区间段

那咱们讲讲递归部分

进入第一个函数递归之后 先完成第一个函数 再完成第二个函数的递归

因为你只能先完成一个函数的调用再进行下一个函数的调用

这个调用的实现逻辑其实是先完成一个分支再完成下一个分支

也就是先将10 和 6 排序之后 再调用下一个函数 进行排序 7 和1 如此反复

不是一上来就是上面的图 他有个先后顺序 从左到右依次进行 区间段由小到大 逐渐增大

你可以有次序的画一画 这个递归思路 我认为上图可以让你理解清楚

如果不清楚 可能是你递归思路有点乱 理理你的思路 一步一步按照代码去画 应该会很好理解

void _MergeSort(int* a, int left, int right, int* tmp)
{
    //递归 肯定不能死循环下去 需要条件让它停止
    if (left >= right)
    {
        return;
    }

    //int mid = (left + right) / 2;
    int mid = left + (right - left)/2;防止溢出 int 有最大值
    // [left, mid] [mid+1, right] 有序
    _MergeSort(a, left, mid, tmp);//先进入第一个函数不断递归 不断靠左边递归 再慢慢向右递归
    _MergeSort(a, mid + 1, right, tmp);

    int begin1 = left, end1 = mid;
    int begin2 = mid+1, end2 = right;
    int i = left;//定位两个区间段比较之后在tmp数组中的开头位置
    while (begin1 <= end1 && begin2 <= end2)
    {
        if (a[begin1] < a[begin2])
        {
            tmp[i++] = a[begin1++];
        }
        else
        {
            tmp[i++] = a[begin2++];
        }
    }

    while (begin1 <= end1)
    {
        tmp[i++] = a[begin1++];
    }

    while (begin2 <= end2)
    {
        tmp[i++] = a[begin2++];
    }

    // tmp 数组拷贝回a
    for (int j = left; j <= right; ++j)
    {
        a[j] = tmp[j];
    }
}

void MergeSort(int* a, int n)
{
    int* tmp = (int*)malloc(sizeof(int)*n);
    if (tmp == NULL)
    {
        printf("malloc fail\n");
        exit(-1);
    }

    _MergeSort(a, 0, n - 1, tmp);

    free(tmp);
    tmp = NULL;
}

以上就是归并排序 这块需要你去在纸上画画理解理解递归 如果不能理解 你可以试试理解非递归版本 ~

如有不懂 留言私信~

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

菜鸡爱玩

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

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

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

打赏作者

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

抵扣说明:

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

余额充值