归并排序的确是分治思想的经典代表。写了很多次,这次又有新的收获,过去用的是递归的实现方式,理论上任何用递归方法实现的代码都可以转换为非递归的形式,所以此例也不例外。然后再用非递归的实现方法上进行改进,完成了“自然归并”算法,这比直接归并效率要高一些。

先给出基础的用递归方法实现的归并排序:

View Code

#include<iostream>using namespace std;const int SIZE = 100;int arr[SIZE];//排序数组arr[fir:end]void mergeSort(int fir,int end){//当子序列就只有一个元素的时候就弹出    if(fir==end)return;

//分治    int mid = (fir+end)/2;    mergeSort(fir,mid);    mergeSort(mid+1,end);

//合并    int tempArr[SIZE];int fir1=fir,fir2=mid+1;for(int i=fir;i<=end;i++){if(fir1>mid)            tempArr[i]=arr[fir2++];else if(fir2>end)            tempArr[i]=arr[fir1++];else if(arr[fir1]>arr[fir2])            tempArr[i]=arr[fir2++];else             tempArr[i]=arr[fir1++];    }for(int i=fir;i<=end;i++)        arr[i]=tempArr[i];

}int main(){//测试    int n;cin>>n;for(int i=0;i<n;i++)cin>>arr[i];    mergeSort(0,n-1);for(int i=0;i<n;i++)cout<<arr[i]<<" ";    cout<<endl;return 0;}

其中归并函数中的合并方法没有单独写开,单独函数merge()及其解释如下:

void merge(int fir,int end,int mid){//合并    int tempArr[SIZE];int fir1=fir,fir2=mid+1;for(int i=fir;i<=end;i++){if(fir1>mid)//前半段扫描完毕            tempArr[i]=arr[fir2++];else if(fir2>end)//后半段扫描完毕            tempArr[i]=arr[fir1++];//两端如果都没有扫描完毕的话//就选择较小的值插在临时数组的后端        else if(arr[fir1]>arr[fir2])            tempArr[i]=arr[fir2++];else             tempArr[i]=arr[fir1++];    }//将排好的临时数组拷贝到原数组中,返回    for(int i=fir;i<=end;i++)        arr[i]=tempArr[i];}

有了merge函数后mergesort1()如下:

void mergeSort(int fir,int end){//当子序列就只有一个元素的时候就弹出    if(fir==end)return;

//分治,现分为两个子段,    int mid = (fir+end)/2;    mergeSort(fir,mid);//对左半段递归排序    mergeSort(mid+1,end);//对右半段递归排序

//合并    merge();

}

归并排序的非递归实现如下,思想和递归正好相反,原来的递归过程是将待排序集合一分为二,直至排序集合就剩下一个元素位置,然后不断的合并两个排好序的数组。所以非递归思想为,将数组中的相邻元素两两配对。用merge函数将他们排序,构成n/2组长度为2的排序好的子数组段,然后再将他们排序成长度为4的子数组段,如此继续下去,直至整个数组排好序。

代码如下:(和书上不同,自认为更好理解一些)

void mergeSort2(int n){int s=2,i;while(s<=n){        i=0;while(i+s<=n){            merge(i,i+s-1,i+s/2-1);            i+=s;        }//处理末尾残余部分        merge(i,n-1,i+s/2-1);        s*=2;    }//最后再从头到尾处理一遍    merge(0,n-1,s/2-1);}

自然合并排序

该排序需要一个叫做pass()的子函数,该函数通过一次扫描,将排序前数组中已经有序的子数组段信息记录在rec[]数组中,然后返回原数组中自然序列的个数。

该算法的实现示意图见课本P23笔记。

// 自然归并是归并排序的一个变形,效率更高一些,可以在归并排序非递归实现的基础上进行修改//对于已经一个已经给定数组a,通常存在多个长度大于1的已经自然排好的子数组段//因此用一次对数组a的线性扫描就可以找出所有这些排好序的子数组段//然后再对这些子数组段俩俩合并//代码的实现如下:#include<iostream>using namespace std;const int SIZE = 100;int arr[SIZE];int rec[SIZE];//记录每个子串的起始坐标//排序数组arr[fir:end]//合并操作的子函数void merge(int fir,int end,int mid);//扫描得到子串的子函数int pass(int n);//自然合并函数void mergeSort3(int n);/********************************************************************/

void mergeSort3(int n){int num=pass(n);while(num!=2){//num=2说明已经排好序了//每循环一次,进行一次pass()操作        for(int i=0;i<num;i+=2)//坐标解释可参加P23页的图示            merge(rec[i],rec[i+2]-1,rec[i+1]-1);        num=pass(n);    }}void merge(int fir,int end,int mid){//合并    int tempArr[SIZE];int fir1=fir,fir2=mid+1;for(int i=fir;i<=end;i++){if(fir1>mid)            tempArr[i]=arr[fir2++];else if(fir2>end)            tempArr[i]=arr[fir1++];else if(arr[fir1]>arr[fir2])            tempArr[i]=arr[fir2++];else             tempArr[i]=arr[fir1++];    }for(int i=fir;i<=end;i++)        arr[i]=tempArr[i];}int  pass(int n){int num=0;int biger=arr[0];    rec[num++]=0;for(int i=1;i<n;i++){if(arr[i]>=biger)biger=arr[i];else {            rec[num++]=i;            biger=arr[i];        }    }//给rec[]加一个尾巴,方便排序    rec[num++]=n;return num;}int main(){int n;while(cin>>n){for(int i=0;i<n;i++)cin>>arr[i];//测试mergeSort函数        /**/mergeSort3(n);for(int i=0;i<n;i++)cout<<arr[i]<<" ";        cout<<endl;

//测试pass函数        /*int num = pass(n);        for(int i=0;i<num;i++)cout<<rec[i]<<" ";        cout<<endl;*/    }return 0;}

转载于:https://www.cnblogs.com/liushang0419/archive/2011/09/19/2181476.html

归并排序(递归实现+非递归实现+自然合并排序)相关推荐

  1. java 归并排序 非递归_归并排序-递归及非递归的JAVA实现

    归并排序介绍 平均时间复杂度: O(NLogN) 最好情况时间复杂度: O(NLogN) 最差情况时间复杂度: O(NLogN) 所需要额外空间: 递归:O(N + LogN), 非递归:O(N) 稳 ...

  2. 快速排序和归并排序中一趟的理解(递归和非递归)

    引:2019年408中数据结构一道考察快速排序的选择题 答案:D 定位:这道题在考察快速排序中一趟的概念.注意,基本的冒泡,插入,选择排序的一趟概念很容易理解, 接下来我们要讨论的是递归排序算法中(本 ...

  3. 【排序】详细聊聊归并排序(含非递归)

    目录 归并排序的基本思想: 递归算法: 递归算法的思路分析: 开辟数组的函数: 递归的函数: 非递归算法: 非递归的思路分析: 边界问题: 时间复杂度和空间复杂度分析: 归并排序的基本思想: 归并排序 ...

  4. 归并排序的python实现:递归与非递归

    递归 原理比较简单,就是有序数组的合并. def merge(a, b):c = []i = j = 0while i < len(a) and j < len(b):if a[i] &l ...

  5. python创建树结构、求深度_数据结构-树以及深度、广度优先遍历(递归和非递归,python实现)...

    前面我们介绍了队列.堆栈.链表,你亲自动手实践了吗?今天我们来到了树的部分,树在数据结构中是非常重要的一部分,树的应用有很多很多,树的种类也有很多很多,今天我们就先来创建一个普通的树.其他各种各样的树 ...

  6. 二叉树的几种递归和非递归式遍历:

    二叉树的几种递归和非递归式遍历: 1 #include <fstream> 2 #include <iostream> 3 4 using namespace std; 5 6 ...

  7. 全排列(含递归和非递归的解法)

    全排列在近几年各大网络公司的笔试中出现的比较频繁 首先来看看题目是如何要求的(百度迅雷校招笔试题). 用C++写一个函数, 如 Foo(const char *str), 打印出 str 的全排列, ...

  8. C#实现(递归和非递归)快速排序和简单排序

    C#实现(递归和非递归)快速排序和简单排序 本人因为最近工作用到了一些排序算法,就把几个简单的排序算法,想冒泡排序,选择排序,插入排序,奇偶排序和快速排序等整理了出来,代码用C#代码实现,并且通过了测 ...

  9. 分别用递归和非递归方式实现二叉树先序、中序和后序遍历(java实现)

    分别用递归和非递归方式实现二叉树先序.中序和后序遍历 用递归和非递归方式,分别按照二叉树先序.中序和后序打印所有的节点.我们约定:先序遍历顺序 为根.左.右;中序遍历顺序为左.根.右;后序遍历顺序为左 ...

最新文章

  1. 我的名片能运行Linux和Python,还能玩2048小游戏,成本只要20元
  2. vue--为什么data属性必须是一个函数
  3. I2C总线及AT24C02读写实验报告
  4. python动态参数_python动态参数
  5. 手把手教你做关键词匹配项目(搜索引擎)---- 第二十一天
  6. babylonjs 设置面板位置_一篇关于开关面板的详细集合,值得收藏转发
  7. 使用IPFS集群搭建创建私有IPFS网络
  8. kubernetes 学习 pod相关
  9. Mac开发环境搭建_zsh替换bash_2
  10. 【Elasticsearch】Elasticsearch如何实现 SQL语句中 Group By 和 Limit 的功能
  11. 对话指令集创始人兼CEO潘爱民:面向未来的新型物联网操作系统 | 人物志
  12. [导入]关于中文文本的截取
  13. EXCEL 电子游戏销售数据分析项目
  14. 白帽大会资料下载 | 让技术自生长
  15. 计算机怎么快捷截图桌面,计算机屏幕截图快捷方式是获取屏幕截图的四种方法. 如何在笔记本电脑上截图?...
  16. java 日程安排_Java实现 LeetCode 731 我的日程安排表 II(二叉树)
  17. Win11麦克风有回音怎么消除 Win11麦克风回声怎么调
  18. [perl.mak] Error 2
  19. 无计算机基础的学电子化移交,由于目标计算机积极拒绝,无法连接。 127.0.0.1-8080-这是什么意思啊...
  20. VUE实战--网易云音乐

热门文章

  1. linux sudoers_Linux –将用户添加到Sudoers列表
  2. selenium中js定位_Selenium中的定位剂
  3. Java SE 9:Stream API的改进
  4. java从控制台读取字符串_从控制台读取Java字符串
  5. python string_Python String casefold()
  6. java中线程的生命周期_Java中的线程生命周期– Java中的线程状态
  7. iOS UISegmentedControl
  8. jenkins的邮件提醒
  9. 全栈工程师的3个关键技能是什么?
  10. 数据结构【图】—023邻接表深度和广度遍历