归并排序

归并排序是一种基于分而治之的排序技术。最坏情况下的时间复杂度为O(nlogn),它是最受人尊敬的算法之一。归并排序首先将数组分成相等的两半,然后以排序的方式将它们合并。

核心思想

为了理解合并排序,我们采用未排序的数组,如下所示

我们知道归并排序首先将整个数组迭代地分成相等的一半,除非获得原子值。我们在这里看到一个由8个项目组成的数组分为两个大小为4的数组。

这不会更改原件中项目出现的顺序。现在我们将这两个数组分为两半。

我们进一步划分这些数组,并获得无法再划分的原子值

现在,我们将它们分解时的方式完全相同。请注意提供给这些列表的颜色代码。

我们首先比较每个列表的元素,然后以排序的方式将它们组合到另一个列表中。我们看到14和33处于排序位置。我们比较27和10,在2个值的目标列表中,我们先放置10,然后是27。我们更改19和35的顺序,而将42和44顺序放置。

在合并阶段的下一个迭代中,我们比较两个数据值的列表,然后将它们合并为找到的数据值的列表,将所有数据按排序顺序放置。

最终合并后,列表应如下所示:

代码开发

实现思路

归并排序会继续将列表分为相等的一半,直到无法再对其进行划分为止。根据定义,如果它只是列表中的一个元素,则会对其进行排序。然后,合并排序将合并较小的排序列表,同时也将新列表排序。

Step 1−将列表递归分为两半,直到无法再将其划分为止。
Step 2−将若干个组两两合并,保证合并后的序列有序。
step 3-重复第二步操作直到只剩下一组,排序完成。

伪代码

package lipan.top.notes.basicsort.impl;import lipan.top.notes.basicsort.ISort;import java.util.Arrays;/*** <p/>* <li>title: 基础排序-归并排序</li>* <li>@author: li.pan</li>* <li>Date: 2019/12/7 12:15 下午</li>* <li>Version: V1.0</li>* <li>Description: </li>*/
public class MergeSort implements ISort<Integer> {@Overridepublic void sort(Integer[] arr) {int n = arr.length;sort(arr, 0, n - 1);}//递归使用归并排序,对arr[l....r]的范围进行进行排序 0,10 0/5 0/2private static void sort(Integer[] arr, int l, int r) {System.out.println("sort: arr=" + Arrays.toString(arr) + ",l=" + l + ",r=" + r);if (l >= r) { //当子序列中只有一个元素递归到底的情况System.out.println("------------------------------------------------------------------------------------------------");return;}int mid = (l + r) / 2;  // l=0 r=2sort(arr, l, mid);sort(arr, mid + 1, r);merge(arr, l, mid, r);}//将arr[l...mid]和arr[mid+1...r]两部分进行归并private static void merge(Integer[] arr, int l, int mid, int r) {System.out.println("merge: arr=" + Arrays.toString(arr) + ",l=" + l + ",mid=" + mid + ",r=" + r);// 开辟临时空间,合并左半部分已经排好序的数组和右半部分已经排好序的数组Integer[] aux = Arrays.copyOfRange(arr, l, r + 1);System.out.println("merge: arr=" + Arrays.toString(aux) + ",l=" + l + ",mid=" + mid + ",r=" + r);// 初始化,i指向左半部分的起始位置索引;j指向右半部分起始索引位置mid+1int i = l, j = mid + 1;for (int k = l; k <= r; k++) {  // k指向两个元素比较后归并下一个需要放置的位置System.out.println("第K=" + k + "趟");/*** 考虑数组越界*/if (i > mid) {  // 如果左半部分元素已经全部处理完毕,arr[k] = aux[j - l];j++;} else if (j > r) {   // 如果右半部分元素已经全部处理完毕arr[k] = aux[i - l];i++; // l表示偏移}/*** 真正比较: 两两比较放入新的序列arr*/else if (aux[i - l] < aux[j - l]) {  // 左半部分所指元素 < 右半部分所指元素arr[k] = aux[i - l];i++;} else {  // 左半部分所指元素 >= 右半部分所指元素(右边的放置待排序前面)arr[k] = aux[j - l];j++;}}System.out.println("\n");}}

执行结果

------------------------------随机数组--------------------------------
插入排序测试1数据量为100排序时长为: 0.001s
插入排序测试2数据量为10000排序时长为: 0.084s
插入排序测试3数据量为100000排序时长为: 5.868s
冒泡排序测试1数据量为100排序时长为: 0.0s
冒泡排序测试2数据量为10000排序时长为: 0.061s
冒泡排序测试3数据量为100000排序时长为: 9.069s
希尔排序测试1数据量为100排序时长为: 0.0s
希尔排序测试2数据量为10000排序时长为: 0.004s
希尔排序测试3数据量为100000排序时长为: 0.008s

代码优化

  • 虽然归并排序是nlogn级别的算法, 但是在数组数据量比较小的时候, 插入排序的效率仍然是高于归并排序的, 所以可以在对数组分解到足够小之后, 使用插入排序, 然后再递归进行归并排序。

  • 如果一个数组是近乎有序的, 或者说是完全有序的, 上述步骤会有很多无用的merge操作, 所以可以在进行merge前增加一个判断, 效率也会有一定的提高。

伪代码

package lipan.top.notes.basicsort.impl.optimize;import lipan.top.notes.basicsort.ISort;import java.util.Arrays;/*** <p/>* <li>title: 归并排序优化</li>* <li>@author: li.pan</li>* <li>Date: 2019/12/7 12:15 下午</li>* <li>Version: V1.0</li>* <li>Description:* 优化1: 对于arr[mid] <= arr[mid+1]的情况,不进行merge* 优化2: 对于小规模数组, 使用插入排序* </li>*/
public class MergeSortOptimize implements ISort<Integer> {@Overridepublic void sort(Integer[] arr) {int n = arr.length;sort(arr, 0, n - 1);}//递归使用归并排序,对arr[l....r]的范围进行进行排序private static void sort(Integer[] arr, int l, int r) {/***  优化2: 对于小规模数组, 使用插入排序*/if (r - l <= 15) {insertionSort(arr, l, r);return;}int mid = (l + r) / 2;sort(arr, l, mid);sort(arr, mid + 1, r);/*** 优化1: 对于arr[mid] <= arr[mid+1]的情况,不进行merge* 对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失*/if (arr[mid] > arr[mid + 1])merge(arr, l, mid, r);}//将arr[l...mid]和arr[mid+1...r]两部分进行归并private static void merge(Integer[] arr, int l, int mid, int r) {// 开辟临时空间,合并左半部分已经排好序的数组和右半部分已经排好序的数组Integer[] aux = Arrays.copyOfRange(arr, l, r + 1);// 初始化,i指向左半部分的起始位置索引;j指向右半部分起始索引位置mid+1int i = l, j = mid + 1;for (int k = l; k <= r; k++) {  // k指向两个元素比较后归并下一个需要放置的位置/*** 考虑数组越界*/if (i > mid) {  // 如果左半部分元素已经全部处理完毕,arr[k] = aux[j - l];j++;} else if (j > r) {   // 如果右半部分元素已经全部处理完毕arr[k] = aux[i - l];i++; // l表示偏移}/*** 真正比较*/else if (aux[i - l] < aux[j - l]) {  // 左半部分所指元素 < 右半部分所指元素arr[k] = aux[i - l];i++;} else {  // 左半部分所指元素 >= 右半部分所指元素arr[k] = aux[j - l];j++;}}}// 对arr[l...r]的区间使用InsertionSort排序public static void insertionSort(Integer[] arr, int l, int r) {for (int i = l + 1; i <= r; i++) {Integer e = arr[i];int j = i;for (; j > l && arr[j - 1] > e; j--)arr[j] = arr[j - 1];arr[j] = e;}}}

执行结果

------------------------------随机数组--------------------------------
插入排序测试1数据量为100排序时长为: 0.001s
插入排序测试2数据量为10000排序时长为: 0.12s
插入排序测试3数据量为100000排序时长为: 8.578s
归并排序测试1数据量为100排序时长为: 0.0s
归并排序测试2数据量为10000排序时长为: 0.013s
归并排序测试3数据量为100000排序时长为: 0.025s
归并排序优化测试1数据量为100排序时长为: 0.0s
归并排序优化测试2数据量为10000排序时长为: 0.001s
归并排序优化测试3数据量为100000排序时长为: 0.003s
------------------------------近乎有序数组--------------------------------
插入排序测试1数据量为100排序时长为: 0.0s
插入排序测试2数据量为10000排序时长为: 0.039s
插入排序测试3数据量为100000排序时长为: 2.764s
归并排序测试1数据量为100排序时长为: 0.0s
归并排序测试2数据量为10000排序时长为: 0.0s
归并排序测试3数据量为100000排序时长为: 0.009s
归并排序优化测试1数据量为100排序时长为: 0.0s
归并排序优化测试2数据量为10000排序时长为: 0.0s
归并排序优化测试3数据量为100000排序时长为: 0.0s

(五) 数据结构 - 归并排序相关推荐

  1. 数据结构----归并排序

    数据结构----归并排序 原理:参考趣学数据结构 代码: #include<stdio.h> #include<stdlib.h> #define N 100 void gui ...

  2. 实验五 数据结构综合应用 20162305

    实验五 数据结构综合应用 20162305 0 分析系统架构 我们本次做的是一个飞机大战的游戏,本次游戏是想让使用者通过操控一个飞机进行击毁敌机和躲避敌机的操作.这个APP总体是基于精灵类ISpirt ...

  3. 20162316刘诚昊 实验五-数据结构综合应用

    20162316刘诚昊 2017-2018-2 <Java程序设计>第五次实验 数据结构综合应用 实验链接: 实验五 Android Studio遇到的错误-1 实验五 图的实现与应用-2 ...

  4. 数据结构---归并排序和外部排序

    内部排序 若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序. 外部排序 若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序. 就地排序 若排序算 ...

  5. 数据结构 归并排序 C++

    个人对归并排序的理解 1.也是分治法 2.先拆分,拆分总序列, (先不考虑奇数) 第一次拆分为N个子序列, 每个子序列元素有1个,俩俩合并子序列 第二次拆分为N/2子序列,每个子序列元素有2个, 俩俩 ...

  6. python算法与数据结构-归并排序算法

    代码如下所示: # -*-coding=utf-8-*- def merge_sort(alist):"""归并排序"""n = len(a ...

  7. 数据结构——归并排序

    1. 归并排序 分治模型在每层递归时都有三个步骤: 分解原问题为若干子问题,这些子问题是原问题的规模较小的实例: 解决这些子问题,递归地求解各子问题.然而,若子问题的规模足够小,则直接求解: 合并这些 ...

  8. 数据结构Java版实验五_实验五数据结构综合应用 20162310

    分析系统架构 Sprite精灵类 ISprite精灵类是所有类的父类 CombatAircraft战斗机类 首先确保战斗机完全位于Canvas范围内,每隔7帧发射单发黄色子弹. protected v ...

  9. 简明python教程五----数据结构

    python中有三种内建的数据结构:列表.元组和字典 list是处理一组有序项目的数据结构,即你可以在一个列表中存储一个序列的项目.在python中,每个项目之间用逗号分隔. 列表中的项目应该包括在方 ...

最新文章

  1. c语言程序计算bmi指数,计算BMI的程序怎么写
  2. Delete、Upadate、Insert事件触发常见错误
  3. plotly基于dataframe数据绘制股票蜡烛图(Candlestick)
  4. python变量作用域图解_python的变量作用域问题
  5. 初学者学MvcMovie遇到的问题解决办法
  6. Android Studio使用心得
  7. 类的静态成量变量必须初始化
  8. 网页边框和网页文字阴影
  9. Ssm在线商城系统实战开发
  10. codeforces 707D-(DFS+bitset)
  11. C#获取 Flv视频文件播放时间长度等信息
  12. 关于semantic-ui的cdn失效问题(怎样通过本地引用semantic-ui)
  13. 《华为工作法》学习笔记
  14. 小米笔记本怎么恢复出厂的系统
  15. 【图论】Graph Fourier Transform
  16. gamit如何添加新的天线类型
  17. 南京大学计算机实验教程,南京大学 计算机系统基础 课程实验 2018(PA2)
  18. c语言oj中的wa是什么意思,杭电oj1002题一直是WA ,然而格式什么的都对啊?求教OAO...
  19. 【论文翻译】:(arxiv 2022)PS-NeRV: Patch-wise Stylized Neural Representations for Videos
  20. 知识工程重点知识介绍-1

热门文章

  1. Windows 计算机上查看 DNS 缓存的方法
  2. java程序调用时的调用规则,Java程序调用ILog规则出错
  3. 苹果开发者账号(公司级)和邓白氏编码(D-U-N-S)申请记录(2015.06)
  4. bh1750c语言程序,BH1750FVI数字光线强度传感器 51单片机源程序
  5. Word文档粘贴的表格中文字有淡灰色背景的去除方法
  6. 如何用好erp系统【ERP】
  7. DRM驱动(六)之atomic_check
  8. springboot-admin 整合nacos处理含有context-path的应用问题
  9. python中有哪几种注释方式_python的注释有哪几种
  10. GIT fatal: unable to auto-detect email address (got 'Administrator@DESKTOP-880G72L.(none)')