归并排序

定义

归并排序是采用分治法的一个非常典型的应用,将已有序的子序列合并,得到完全有序的序列,即先使每个子序列有序,再使子序列段间有序
若将两个有序表合并成一个有序表,称为二路归并

稳定性

稳定

时间复杂度

O(N*logN)

代码

测试用例

int arr[] = {2,4,3,1,6,5};

原代码

    /*** 归并排序(重载)* 为了创建临时数组 完成数组的复用** @param arr 无序数组*/public static void mergeSort(int[] arr){System.out.println("初始数据" + Arrays.toString(arr));int[] temp = new int[arr.length];mergeSort(arr,temp,0,arr.length-1);}/*** 归并排序* @param arr* @param temp* @param start* @param end*/public static void mergeSort(int[] arr,int[] temp,int start,int end){if (start >= end){return;}int mid = (start + end) / 2;// 排序第一个数组mergeSort(arr,temp,start,mid);// 排序第二个数组mergeSort(arr,temp,mid+1,end);// 合并两个有序数组merge(arr,temp,start,mid,end);}

运行结果
(输出语句见 二路归并 算法)

扩展1 二路归并

    /*** 二路归并 合并两个有序序列* 现将两个有序序列合并成一个序列 然后开始排序* @param arr   存放两个有序列表的数组* @param temp  临时数组* @param start 左侧有序列表开始的下标* @param mid   左侧结束位置下标  右侧开始位置下标:mid+1* @param end   右侧有序列表结束下标*/public static void merge(int[] arr,int[] temp,int start,int mid,int end){System.out.println("前一组开始下标 "+start);System.out.println("前一组结束下标 "+mid);System.out.println("后一组开始下标 "+(mid+1));System.out.println("后一组结束下标 "+end);int i = start; // i 用来记录 第一个数组的遍历位置int j = mid + 1; // 用来记录第二个数组的遍历位置int k = start; // 用来表示临时数组的下标// i=mid+1:第一个数组先遍历完成// j=end+1:第二个数组先遍历完成while (i != mid +1 && j != end +1){if (arr[i]>arr[j]){temp[k++] = arr[j++];}else {temp[k++] = arr[i++];}}// 第一个数组没有遍历完毕while (i != mid + 1){temp[k++] = arr[i++];}// 第二个元素没有遍历完毕while (j != end + 1){temp[k++] = arr[j++];}for (int n = start;n<=end;n++){arr[n] = temp[n];}System.out.println("本次合并结果:"+Arrays.toString(arr));}

扩展二 递归的时间复杂度

master公式

T(N) = a * T(Nb{N\over b}bN​ ) + O(Nd)

  • N:数据样本量
  • a:子过程调用次数
  • Nb{N\over b}bN​:子过程数据样本量
  • O(Nd):除去递归之外的时间复杂度

递归时间复杂度

扩展三 应用

数组小和问题

题面
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和
示例
[2,3,4,1,5] 小和为17
- 2左边比其小的数:
- 3左边比其小的数:2
- 4左边比其小的数:2 3
- 1左边比其小的数:
- 5左边比其小的数:2 3 4 1
思路
求数组中每个数左侧比其小的所有数的和,等同于求每个数右侧比其大的数的数量乘以这个数的和进行累加
代码实现

/*** 求数组小和* @param arr*/public static void minSum(int[] arr){if (arr == null || arr.length<2){System.out.println("所求小和为 0");return;}int[] temp = new int[arr.length];int minsum = mergeSort4MinSum(arr,temp,0,arr.length-1);System.out.println("所求小和值为 "+minsum);}/*** 求数组小和* @param arr* @param temp* @param start* @param end* @return*/public static int mergeSort4MinSum(int[] arr,int[] temp,int start,int end){if (start >= end){return 0;}int mid = (start + end) / 2;// 排序第一个数组 计算第一个数组小和int leftMinSum = mergeSort4MinSum(arr,temp,start,mid);// 排序第二个数组 计算第二个数组小和int rughtMinSum = mergeSort4MinSum(arr,temp,mid+1,end);// 合并两个有序数组 计算总的数组小和int allMinSum = merge4MinSum(arr,temp,start,mid,end);return leftMinSum + rughtMinSum +allMinSum;}/*** 求数组小和* @param arr   存放两个有序列表的数组* @param temp  临时数组* @param start 左侧有序列表开始的下标* @param mid   左侧结束位置下标  右侧开始位置下标:mid+1* @param end   右侧有序列表结束下标* @return 本组小和*/public static int merge4MinSum(int[] arr,int[] temp,int start,int mid,int end){System.out.println("前一组开始下标 "+start);System.out.println("前一组结束下标 "+mid);System.out.println("后一组开始下标 "+(mid+1));System.out.println("后一组结束下标 "+end);int i = start; // i 用来记录 第一个数组的遍历位置int j = mid + 1; // 用来记录第二个数组的遍历位置int k = start; // 用来表示临时数组的下标int minSumResult = 0;// i=mid+1:第一个数组先遍历完成// j=end+1:第二个数组先遍历完成while (i != mid +1 && j != end +1){if (arr[i]>arr[j]){temp[k++] = arr[j++];}else {minSumResult += arr[i]<arr[j]?(end-j+1) * arr[i]:0; // 计算小和语句temp[k++] = arr[i++];}}// 第一个数组没有遍历完毕while (i != mid + 1){temp[k++] = arr[i++];}// 第二个元素没有遍历完毕while (j != end + 1){temp[k++] = arr[j++];}for (int n = start;n<=end;n++){arr[n] = temp[n];}System.out.println("本次合并结果:"+Arrays.toString(arr));return minSumResult;}

逆序对和问题

题面
在一个数组中,左边的数如果比右边的数大,则两个数构成一个逆序对,请输出逆序对的数量
示例
[2,3,4,1,5,6]中逆序对的数量为3,分别为(2,1) (3,1) (4,1)
思路
即求每个数右边比其小的个数的和
代码实现


/*** @Classname AntiOrder* @Description 将归并的从小到大排序 换成 从大到小排序**/
public class AntiOrder {// 记录逆序对信息private static Map<String,Object> antiOrderMap = new HashMap<>();public static void main(String[] args) {// 测试数据int[] arr = {2,4,3,1,6,5};antiOrder(arr);}/*** 求逆序对* @param arr*/public static void antiOrder(int[] arr){if (arr == null || arr.length<2){System.out.println("逆序对的数量为 0");return;}int[] temp = new int[arr.length];int minsum = mergeSort4AntiOrder(arr,temp,0,arr.length-1);System.out.println("逆序对的数量为  "+minsum);System.out.println("逆序对如下:");System.out.println(antiOrderMap.toString());antiOrderMap = new HashMap<>();}/*** 求逆序对* @param arr* @param temp* @param start* @param end* @return*/public static int mergeSort4AntiOrder(int[] arr,int[] temp,int start,int end){if (start >= end){return 0;}int mid = (start + end) / 2;// 排序第一个数组 计算第一个数组中逆序对int leftAnti = mergeSort4AntiOrder(arr,temp,start,mid);// 排序第二个数组 计算第二个数组中逆序对int rughtAnti = mergeSort4AntiOrder(arr,temp,mid+1,end);// 合并两个有序数组 计算总的数组中逆序对int allAnti = merge4AntiOrder(arr,temp,start,mid,end);return leftAnti + rughtAnti +allAnti;}/*** 求逆序对* @param arr   存放两个有序列表的数组* @param temp  临时数组* @param start 左侧有序列表开始的下标* @param mid   左侧结束位置下标  右侧开始位置下标:mid+1* @param end   右侧有序列表结束下标* @return 本组小和*/public static int merge4AntiOrder(int[] arr,int[] temp,int start,int mid,int end){System.out.println("前一组开始下标 "+start);System.out.println("前一组结束下标 "+mid);System.out.println("后一组开始下标 "+(mid+1));System.out.println("后一组结束下标 "+end);int i = start; // i 用来记录 第一个数组的遍历位置int j = mid + 1; // 用来记录第二个数组的遍历位置int k = start; // 用来表示临时数组的下标int antiResult = 0;// i=mid+1:第一个数组先遍历完成// j=end+1:第二个数组先遍历完成while (i != mid +1 && j != end +1){if (arr[i]<arr[j]){temp[k++] = arr[j++];}else {// 统计逆序对if (arr[i]>arr[j]){antiResult += end-j+1; // 计算逆序对数量// 逆序对内容for (int index = j;index<=end;index++){antiOrderMap.put(antiOrderMap.size()==0?"1":(antiOrderMap.size()+1)+"","("+arr[i]+","+arr[index]+")");}}temp[k++] = arr[i++];}}// 第一个数组没有遍历完毕while (i != mid + 1){temp[k++] = arr[i++];}// 第二个元素没有遍历完毕while (j != end + 1){temp[k++] = arr[j++];}for (int n = start;n<=end;n++){arr[n] = temp[n];}System.out.println("本次合并结果:"+ Arrays.toString(arr));return antiResult;}
}

运行结果

瑞士轮问题

题面
在双⼈对决的竞技性⽐赛,如乒乓球、⽻⽑球、国际象棋中,最常⻅的赛制是淘汰赛和循环赛。前者
的特点是⽐赛场数少,每场都紧张刺激,但偶然性较⾼。后者的特点是较为公平,偶然性较低,但⽐
赛过程往往⼗分冗⻓。

本题中介绍的瑞⼠轮赛制,因最早使⽤于1895年在瑞⼠举办的国际象棋⽐赛⽽得名。它可以看作是淘
汰赛与循环赛的折衷,既保证了⽐赛的稳定性,⼜能使赛程不⾄于过⻓。

2*N 名编号为 1~2N 的选⼿共进⾏ R 轮⽐赛。每轮⽐赛开始前,以及所有⽐赛结束后,都会对选⼿
进⾏⼀次排名。排名的依据是选⼿的总分。选⼿的总分为第⼀轮开始前的初始分数加上已参加过的所
有⽐赛的得分和。总分相同的,约定编号较⼩的选⼿排名靠前。
每轮⽐赛的对阵安排与该轮⽐赛开始前的排名有关:第1名和第2名、第3名和第4名、……、第 2K-1
名和第 2K 名、…… 、第 2N-1 名和第 2N 名,各进⾏⼀场⽐赛。每场⽐赛胜者得1分,负者得0
分。也就是说除了⾸轮以外,其它轮⽐赛的安排均不能事先确定,⽽是要取决于选⼿在之前⽐赛中的
表现。
现给定每个选⼿的初始分数及其实⼒值,试计算在 R 轮⽐赛过后,排名第 Q 的选⼿编号是多少。我们
假设选⼿的实⼒值两两不同,且每场⽐赛中实⼒值较⾼的总能获胜。

示例
根据下表3名选⼿实⼒值及初始分数,求取4轮对阵后排名第2的选⼿为:编号1选⼿

思路
⾸先根据初始分数排序,然后在每次对阵结束后分为胜者组和败者组,分组时保证先进⾏对阵的选⼿
在前,后对阵的选⼿在后,这样既保证了胜者组和败者组两组先天有序,再进⾏归并排序的merge操
作,准备下次对阵。时间复杂度:O(N * logN + R * N)
代码实现

to be continued …

【算法】排序_归并排序相关推荐

  1. 数据算法排序之归并排序

    在你渐渐迷失在你的人生道路上的时候,千万不要因为走的太久,而忘记了我们为什么出发,做码农,也要清楚自己如何才能用有效的土地种植出 出色的产品,于是细节就需要把握一下. 如果你有兴趣可以关注一下公众号 ...

  2. 数据结构与算法系列——排序(10)_归并排序

    1. 工作原理(定义) 归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法,指的是将两个已经排序的序列合并成一个序列的操作.该算法是采用分治法(Divide and Conquer ...

  3. java 快速排序算法简单_排序算法java版,速度排行:冒泡排序、简单选择排序、直接插入排序、折半插入排序、希尔排序、堆排序、归并排序、快速排序......

    先推荐一篇关于排序算法的文章:http://www.cppblog.com/guogangj/archive/2009/11/13/100876.html 本文思路部分来源于上篇文章,但测得的结果似乎 ...

  4. shell sort 最后一列排序_十个必知的排序算法|Python实例系列[1]

    实例内容: 十个必知的排序算法具体代码,并简略的得知每种算法对于不同长度数列的排序时间 十大排序: 1.冒泡排序2.选择排序3.插入排序4.希尔排序5.归并排序6.快速排序7.堆排序8.计数排序9.桶 ...

  5. 使用qsort对不连续的内存数据排序_一点就懂的十大经典排序算法

    我是CSDN博主「雨夜※繁华」,这是我的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:一点就懂的经典十大排序算法_大数据_LH is programmin ...

  6. 在路上---学习篇(一)Python 数据结构和算法 (4) --希尔排序、归并排序

    独白: 希尔排序是经过优化的插入排序算法,之前所学的排序在空间上都是使用列表本身.而归并排序是利用增加新的空间,来换取时间复杂度的减少.这俩者理念完全不一样,注定造成的所消耗的时间不同以及空间上的不同 ...

  7. Algorithm:C++语言实现之内排序、外排序相关算法(插入排序 、锦标赛排序、归并排序)

    Algorithm:C++语言实现之内排序.外排序相关算法(插入排序 .锦标赛排序.归并排序) 目录 一.内排序 1.插入排序 2.锦标赛排序 3.归并排序 二.外排序 1.过程 一.内排序 1.插入 ...

  8. 图解排序算法(四)之归并排序

    图解排序算法(四)之归并排序 基本思想 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide) ...

  9. 排序中减治法算法伪代码_算法浅谈——分治算法与归并、快速排序(附代码和动图演示)...

    在之前的文章当中,我们通过海盗分金币问题详细讲解了递归方法. 我们可以认为在递归的过程当中,我们通过函数自己调用自己,将大问题转化成了小问题,因此简化了编码以及建模.今天这篇文章呢,就正式和大家聊一聊 ...

  10. list 排序_十个必知的排序算法|Python实例系列

    十大排序: 1.冒泡排序2.选择排序3.插入排序4.希尔排序5.归并排序6.快速排序7.堆排序8.计数排序9.桶排序10.基数排序 完整代码和注释如下 # -*- coding: UTF-8 -*-# ...

最新文章

  1. pytorch 多GPU训练
  2. 我用 PyTorch 复现了 LeNet-5 神经网络(CIFAR10 数据集篇)!
  3. 在Java生成的html页面加水印,Java在Excel中添加水印的实现(单一水印、平铺水印)...
  4. 转贴 周鸿祎充其量算作一个低级商人
  5. linux命令行sip电话,基于Linux和MiniGUI的SIP电话终端设计
  6. c语言变量加常量,C语言(二)---常量与变量(示例代码)
  7. oracle 怎么备份sqlserver数据库,Oracle和sqlserver数据库的备份与恢复
  8. vm 虚拟机 删除 权限_虚拟机win7一键傻瓜式安装
  9. robotframework--登录接口,post传递多个参数、及获取content中指定属性的值(5)
  10. 19-3-1Python的PyCharm编辑器,以及格式化输出、while循环、运算符、编码初识
  11. ibeacon的实现,广播数据解析
  12. 数学建模经验谈(四)-参加国赛的几点建议
  13. 深度学习---人脸检测(勿喷)
  14. 62-Mybatis高级介绍
  15. CHM转PDF工具综述
  16. HDU-2121-Ice_cream’s world II
  17. mybatisplus学习之通用的Service(四)
  18. 转载:P值(P-value),“差异具有显著性”和“具有显著差异”
  19. Monte Carlo 与 MCNP、EGS、Geant4的故事
  20. 8月11日云栖精选夜读:阿里云与神州云计算定制混合云解决方案助企业转型

热门文章

  1. (转)周明:未来5-10年,自然语言处理将走向成熟
  2. 阿里云张建锋:数字技术要服务好实体经济
  3. 关于开源软件的十个问题(下篇)
  4. 单片机奇偶交替闪烁_自学单片机第十三篇中:单点交替
  5. 【语音隐写】基于matlab DCT+DWT+SVD音频数字水印嵌入提取【含Matlab源码 1408期】
  6. 【通信】基于matlab Alamouti空频编码【含Matlab源码 801期】
  7. 【优化算法】变异策略的改进型花朵授粉算法【含Matlab源码 480期】
  8. php查询mysql增加模板消息_php 实现发送微信模板消息
  9. 深度神经网络 卷积神经网络_改善深度神经网络
  10. 世界第一个聊天机器人源代码_这是世界上第一个“活着”的机器人