我的编程空间,编程开发者的网络收藏夹
学习永远不晚

归并排序含非递归版

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

归并排序含非递归版

目录

1.归并排序的原理

 2.实现归并排序

2.1框架

2.2区间问题和后序遍历

2.3归并并拷贝

2.4归并排序代码

2.5测试

3.非递归实现归并排序 

3.1初次实现

3.2测试 

3.3修改

 3.4修改测试


1.归并排序的原理

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;

即先使每个子序列有序,再使子序列段间有序   若将两个有序表合并成一个有序表,称为二路归并。 

可以将数组内容想象成一颗二叉树,通过递归的方式将数组的内容分为左子树和右子树,分出来的左子树和右子树又分别有它们的左子树和右子树。不断地向下进行拆分,直到拆分到没有对应区间和区间只有一个元素(有序)的时候便递归返回。然后使用归并的方式将左子树和右子树归并成有序数组,再将其拷贝会原数组,这样对应的子树便会有序,再递归返回,不断地递归。直到根左子树和根右子树有序,再归并和拷贝,整个数组就会变得有序。

如图所示:

 2.实现归并排序

归并排序需要开额外的空间来辅助实现   之所以不在原数组实现,是因为如果你使用交换的方式来进行排序,可能会将原数组已经排好序的部分又一次变回无序,而使用令数组向后移动的方式进行对应的排序,时间复杂度便会大大增加,因此归并排序可以理解成一种以空间换时间的排序方法。

归并排序需要开额外的空间,但每次递归都要开空间显然是不合理的,因此我们使用一个函数来完成递归的部分。思考一下,新创建的函数的参数应该有哪些,首先得有原数组,其次得有我们开辟好的数组,而我们要如二叉树一般形成对应的递归,显然需要区间,而区间的形成需要两个数来辅助,因此可以传递两个代表区间的数进来,可以取名为begin,end(left,right),这样理解起来轻松一些。

2.1框架

2.2区间问题和后序遍历

如二叉树一般实现后序遍历注意: 当begin>=end时代表着区间不存在或者只有一个元素(有序)的时候返回。因为是后序遍历,需要先将区间的分界给计算出来之后才能使用。

2.3归并并拷贝

可以看出,每次递归都会有两个区间的生成[begin,mid]和[mid+1,end]我们的目标就是将这两个区间归并在一起,这个很简单,循环便可以搞定。注意:两个区间不知道是哪个区间先完成循环,因此在外面需要将未完成循环的区间,补充回辅助数组中。

搞定完归并后,使用memcpy将辅助数组中的内容拷贝回原数组,排序便结束了。注意:我们传递的是闭区间,因此拷贝的长度为,end-begin+1

2.4归并排序代码

void MergeSort(int*arr,int n)//将数组和数组的元素个数传递过来{int* a = (int*)malloc(sizeof(int)*n);//创建辅助数组if (a == NULL){perror("malloc fail");return;}_MergeSort(arr,a,0,n-1);    free(a);}void _MergeSort(int*arr,int*a,int begin ,int end){//检验区间是否有效if (begin >= end){return;}//后序遍历int mid = (begin + end) / 2;//新区间为[begin,mid],[mid+1,end]_MergeSort(arr,a,begin,mid);_MergeSort(arr,a,mid+1,end);    //归并int begin1 = begin; int end1 = mid;int begin2 = mid+1; int end2 = end;//两个区间int index = begin;//新区间第一个元素的下标while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] <= arr[begin2]){a[index++] = arr[begin1++];}else{a[index++] = arr[begin2++];}}while (begin1 <= end1){a[index++] = arr[begin1++];}while (begin2 <= end2){a[index++] = arr[begin2++];}//将归并好的内容拷贝回原数组memcpy(arr+begin,a+begin,(end-begin+1)*sizeof(int));}

2.5测试

3.非递归实现归并排序 

根据我们之前的排序我们可以看到它的本质是和预排序差不多的形态,因此我们可以通过一个整型如gap来区分一个元素和一个元素排序,两个元素和两个元素排序.......

3.1初次实现

void MergeSortNonR(int* arr, int n){int* a = (int*)malloc(sizeof(int) * n);//创建辅助数组int gap = 1;//gap=1便是1个元素和1个元素归并while (gap < n){int i = 0;for (i = 0; i < n; i += 2 * gap)//i+=2*gap跳过一整个区间{//两个区间int begin1 = i; int end1 = i + gap - 1;int begin2 = i + gap; int end2 = i + 2 * gap - 1;int index = i;//新区间第一个元素的下标//归并while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] <= arr[begin2]){a[index++] = arr[begin1++];}else{a[index++] = arr[begin2++];}}while (begin1 <= end1){a[index++] = arr[begin1++];}while (begin2 <= end2){a[index++] = arr[begin2++];}memcpy(arr + i, a + i, sizeof(int) * (2 * gap));}gap *= 2;}free(a);}

3.2测试 

出现了越界问题

程序在打印之前就被迫中止了

 

3.3修改

观察可以发现,第一个区间的为[begin1,end1],第二个区间为[begin2,end2],那么begin1必然不会越界,而end1和更后面的begin2,end2都可能会越界。而其中end1和begin2越界的话都在说明已经排好序了,而end2越界,begin2没越界,则在说明还有数据未被排序。

再想一想细节,end1越界了,begin2一定越界,而begin2越界,end1不一定越界,所以无需考虑end1越界,只要begin2越界就说明排序已完成直接break   而只有end2越界,便将end2修正成数组的最大下标即可。

注意:我们之前使用拷贝函数均是拷贝2*gap个过去,在这里显然不合适,总区间长度应修正 为end2-begin1,这个修正不应该放在最后,因为在进行归并的期间,begin1会++至end1   也不应该放在判断begin2,end2越界之前,因为可能会对end2进行修正。综上所述,得出的代码为

void MergeSortNonR(int* arr, int n){int* a = (int*)malloc(sizeof(int) * n);//创建辅助数组int gap = 1;//gap=1便是1个元素和1个元素归并while (gap < n){int i = 0;for (i = 0; i < n; i += 2 * gap)//i+=2*gap跳过一整个区间{//两个区间int begin1 = i; int end1 = i + gap - 1;int begin2 = i + gap; int end2 = i + 2 * gap - 1;if (begin2 >= n){break;}if (end2 >= n)end2 = n - 1;//修正区间长度int order = end2 - begin1+1;//修正要拷贝的长度int index = i;//新区间第一个元素的下标//归并while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] <= arr[begin2]){a[index++] = arr[begin1++];}else{a[index++] = arr[begin2++];}}while (begin1 <= end1){a[index++] = arr[begin1++];}while (begin2 <= end2){a[index++] = arr[begin2++];}memcpy(arr + i, a + i, sizeof(int) * order);}gap *= 2;}free(a);}

 3.4修改测试

至此,归并排序的递归版和非递归版讲解完成,感谢各位友友的来访,祝各位友友前程似锦O(∩_∩)O 

来源地址:https://blog.csdn.net/fq157856469/article/details/133546936

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

归并排序含非递归版

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

c语言递归和非递归排序怎么实现

本篇内容主要讲解“c语言递归和非递归排序怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“c语言递归和非递归排序怎么实现”吧!递归代码流程归并就是把两个或多个序列合并,这里只介绍二路归并,就
2023-06-30

20190516-归并排序

合并两个有序数组中相同的数,输出到一个新的数组中难度分类简单题目描述合并两个有序数组中相同的数,输出到一个新的数组中示例1:输入:nums1 = [1,2,3]nums2 = [2,5,6]输出:[1,2]示例2:输入:nums1 = [1
2023-01-31

python非递归全排列实现方法

刚刚开始学习python,当前看到了函数这一节。结合数组操作,写了个非递归的全排列生成。原理是插入法,也就是在一个有n个元素的已有排列中,后加入的元素,依次在前,中,后的每一个位置插入,生成n+1个新的全排列。因为Python切割数组或者字
2022-06-04

归并排序python实现

归并排序归并排序在于把序列拆分再合并起来,使用分治法来实现,这就意味这要构造递归算法首先是一个例子原序先通过一半一半的拆分,然后:然后再一步一步的向上合并,在合并的过程中完成了排序,合并排序算法如下:def merge(s1,s2,s):
2023-01-31

python排序算法之归并排序

这篇文章主要介绍了python排序算法之归并排序,归并排序算法就是一个先把数列拆分为子数列,对子数列进行排序后,再把有序的子数列合并为完整的有序数列的算法,需要的朋友可以参考下
2023-05-17

什么是Java归并排序

本篇内容主要讲解“什么是Java归并排序”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“什么是Java归并排序”吧!基本介绍归并排序(merge-sort)是利用归并的思想实现的排序方法,该算法采
2023-06-15

PHP 数组快排 vs. 归并排序

快排是一种递归算法,将数组划分成较小元素和较大元素两部分并递归排序,而归并排序将数组递归地分成较小的数组,对每个小数组排序,再合并回原始数组。php 实现的代码分别为:快排:将数组划分为小于和大于基准值的元素,然后对每个部分进行递归排序。归
PHP 数组快排 vs. 归并排序
2024-04-26

Python3实现快速排序、归并排序、堆

# -*- coding: utf-8 -*-# @Time : 2019-03-26 16:46# @Author : Jayce Wong# @ProjectName : leetcode# @FileNa
2023-01-31

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录