归并排序与自然归并排序

归并排序

归并排序(merge - sort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用.将已有的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序.若将两个有序表合并成一个有序表,成为二路归并.

核心步骤讲解

归并排序的核心步骤如下:

可以看出,这算法的核心逻辑就是类似小学语文学文章的一种结构的"总-分-总".

让我们来具体剖析一下,来看看它的思路:

拆分过程:以10    6    7    1这一组为例来看一下拆分过程(核心:使用start, end, mid分别表示开头结尾和中间) :

合并过程:让我么以6,10/1,7这一组为例,来看一下合并过程:最主要的地方就在于传入两组要合并的组,然后创建一个新的数组tmpArr,存放要合并的两个组元素,并将tmpArr中的内容放在源数组中对应位置.

下面是代码:

public class MergeSort {public static void mergeSort(int[] arr) {mergeSortFun(arr, 0, arr.length - 1);}public static void mergeSortFun(int[] arr, int start, int end) {//当start和end重合之后,结束递归if(start >= end) {return;}//找到要切割的位置int mid = (start + end) / 2;//左分支mergeSortFun(arr, start, mid);//右分支mergeSortFun(arr, mid + 1, end);//合并内容merge(arr, start, mid, end);}public static void merge(int[] arr, int left, int mid, int right) {//为了好理解,这里重新用s1,s2,e1,e2表示int s1 = left, e1 = mid, s2 = mid + 1, e2 = right;//定义一个新的数组,用来返回排序好的部分int[] tmpArr = new int[right - left + 1];//用k表示下标(在新创建的数组中的位置)int k = 0;while(s1 <= e1 && s2 <= e2) {if(arr[s1] <= arr[s2]) {tmpArr[k++] = arr[s1++];} else {tmpArr[k++] = arr[s2++];}}//用来存放剩余的部分while(s1 <= e1) {tmpArr[k++] = arr[s1++];}while(s2 <= e2) {tmpArr[k++] = arr[s2++];}//将排序好的数组,放到原来的数组中for(int i = 0; i < tmpArr.length; i++) {arr[i + left] = tmpArr[i];}}public static void main(String[] args) {int[] arr = {10, 6, 7, 1, 3, 9, 4, 2};mergeSort(arr);for(int x : arr) {System.out.print(x + " ");}}
}

归并排序总结

1.归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题.

 2.时间复杂度:O(N * logN)

3.空间复杂度:O(N)

4.稳定度:稳定.

海量数据的排序问题

外部排序:排序过程中需要在磁盘等外部存储进行的排序.

eg:内存只有1G,需要排序的数据有100G

因为内存中因为无法把所有数据全部放下,所以需要外部排序.而归并排序是最常用的外部排序.

1.先把文件切分成200份,每个512M

2.分别对512M内容进行排序,因为每个内存都放得下,所以任意排序都可以

3.进行二路归并,同时对200份有序文件进行归并过程,最终结果就有序了.

自然归并排序

自然归并排序是归并排序的一种变体,其主要特点是利用输入数据的初始有序性.自然归并排序的思想是先找到已经有序的子序列,然后合并这些子序列,直至整个数组有序.

核心步骤讲解

1.查找有序子序列:从数组的起始位置开始,找到第一个有序子序列(递增或递减).这可以通过遍历数组来实现.

2.合并有序子序列:将找到的有序子序列进行合并,在合并的过程中,继续查找下一个有序子序列,直到整个数组有序.

3.重复步骤1和2:重复执行步骤1和2,直到整个数组完全有序.

以下是大致过程:

相对于之前的归并排序,这里就不是单纯的向下递归的过程,而是一个寻找子序列的过程,而在归并的过程中,是基本与之前的一致的.所以这里只需要分析一下寻找子序列的过程即可:

重点:标记的end是上一组的下一个,然后循环时不断标记end,控制为两两一组,进行合并.反复执行上述过程,就可以直接完成.

可能讲的会有点不清楚,请看代码:

import java.util.Arrays;public class NaturalMergeSort {public static void main(String[] args) {int[] array = {10, 6, 7, 1, 3, 9, 4, 2};naturalMergeSort(array);for(int x : array) {System.out.print(x + " ");}}public static void naturalMergeSort(int[] array) {int n = array.length;int[] tempArray = new int[n];int l = -1, m, r;//外部循环,直到不能发现更多的子数组while(l != 0) {l = 0;//内部循环,以寻找并合并有序子数组while (l < n) {m = findNextSortedSubarray(array, l, n, tempArray);//当m 与 n重合时,表明已经找到尾了,退出循环(里和外)if (m == n) {break;}r = findNextSortedSubarray(array, m, n, tempArray);merge(array, l, m, r, tempArray);l = r;}}}//查找下一个有序子数组的方法(返回的是结束位置)private static int findNextSortedSubarray(int[] array, int start, int n, int[] tempArray) {int end = start + 1;while (end < n && array[end - 1] <= array[end]) {end++;}return end;}//二路归并private static void merge(int[] array, int l, int m, int r, int[] tempArray) {int i = l, j = m, k = 0;while (i < m && j < r) {if (array[i] <= array[j]) {tempArray[k++] = array[i++];} else {tempArray[k++] = array[j++];}}while (i < m) {tempArray[k++] = array[i++];}while (j < r) {tempArray[k++] = array[j++];}System.arraycopy(tempArray, 0, array, l, k);}
}

自然归并排序总结 

1.这玩意的思路相比于其它排序其实特别恶心,在生产环境中非常不建议使用,你的项目组成员可能会骂街,说你小子装什么逼??(除非是这种情况:就比如你的数据结构老师不知道是为啥心血来潮让你讲这个,整一个反转课堂啥的,那你也没办法~~)

2.时间复杂度:遍历了一次:O(n)

3.空间复杂度:O(n)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/264969.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AIGC实战——WGAN(Wasserstein GAN)

AIGC实战——WGAN 0. 前言1. WGAN-GP1.1 Wasserstein 损失1.2 Lipschitz 约束1.3 强制 Lipschitz 约束1.4 梯度惩罚损失1.5 训练 WGAN-GP 2. GAN 与 WGAN-GP 的关键区别3. WGAN-GP 模型分析小结系列链接 0. 前言 原始的生成对抗网络 (Generative Adversarial Network, GAN) 在…

C++ Qt开发:如何使用信号与槽

在Qt中&#xff0c;信号与槽&#xff08;Signal and Slot&#xff09;是一种用于对象之间通信的机制。是Qt框架引以为傲的一项机制&#xff0c;它带来了许多优势&#xff0c;使得Qt成为一个强大且灵活的开发框架之一。信号与槽的关联通过QObject::connect函数完成。这样的机制使…

如何使用 Explain 分析 SQL 语句?

如何使用 Explain 分析 SQL 语句&#xff1f; MySQL中EXPLAIN命令是我们分析和优化SQL语句的利器。 如何使用EXPLAIN来分析SQL语句&#xff0c;接下来有15个例子&#xff0c;一起学习呗 1. EXPLAIN的基本使用 EXPLAIN可以用于分析MySQL如何执行一个SQL查询&#xff0c;包括如…

css的Grid布局

1.简单布局 .grid { display: grid; grid-template-columns: 1fr 2fr 1fr; 布局样式 column-gap: 24px; 列间距 row-gap: 24px; 行间距 } 2.排列布局 center垂直方向居中对其 end靠下对齐 3.水平方向对齐 center居中 end靠右对齐 space-between两段对齐 4.对…

你知道LOL中点地面移动是怎么实现的吗?

引言 Cocos中点地面移动的实例。 在游戏开发中&#xff0c;我们经常会遇到通过点击地面控制玩家移动到指定点的需求。 本文将介绍一下如何在Cocos中实现类似LOL的点地面移动效果。 本文源工程在文末获取&#xff0c;小伙伴们自行前往。 点地面移动知识点 要在Cocos中实现类…

【Hadoop_03】HDFS概述与Shell操作

1、集群配置&#xff08;1&#xff09;集群启动/停止方式总结&#xff08;2&#xff09;编写Hadoop集群常用脚本&#xff08;3&#xff09;常考面试题【1】常用端口号【2】常用配置-文件 2、HDFS概述&#xff08;1&#xff09;HDFS产出背景及定义&#xff08;2&#xff09;HDFS…

数字化转型怎么才能做成功?_光点科技

数字化转型对于现代企业来说是一场必要的革命。它不仅仅是技术的更迭&#xff0c;更是企业战略、文化和运营方式全面升级的体现。一个成功的数字化转型能够使企业更具竞争力、更灵活应对市场变化&#xff0c;并最终实现业务增长和效率提升。那么&#xff0c;数字化转型怎么才能…

【开源】基于Vue+SpringBoot的免税店商城管理系统

文末获取源码&#xff0c;项目编号&#xff1a; S 069 。 \color{red}{文末获取源码&#xff0c;项目编号&#xff1a;S069。} 文末获取源码&#xff0c;项目编号&#xff1a;S069。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、系统设计2.1 功能模块设计2.2 研究方法 三、系统…

GridBagLayout GridBagConstraints 笔记231130

实例化使用模板 GridBagLayout gbl new GridBagLayout(); // gbl.columnWidths new int[]{200,200,200}; // 用数组设置列 // gbl.rowHeights new int[]{100,100,100,100,100}; // 用数组设置行GridBagConstraints gbc new GridBagConstraints();/*** gridBagConstrain…

【Spring】Spring统一功能处理

Spring统一功能处理 拦截器拦截器什么是拦截器拦截器的基本使用定义拦截器注册配置拦截器 拦截器详解拦截器的拦截路径配置拦截器实现原理初始化处理请求 适配器模式 统一数据返回格式统一数据返回格式快速入门 统一异常处理 拦截器 场景: 我们要对一个网站实现强制登陆的功能…

TailwindCSS 如何处理RTL布局模式

背景 TikTok作为目前全世界最受欢迎的APP&#xff0c;需要考虑兼容全世界各个地区的本地化语言和阅读习惯。其中对于阿拉伯语、波斯语等语言的阅读书写习惯是从右向左的&#xff0c;在前端有一个专有名字RTL模式&#xff0c;即Right-to-Left。 其中以阿拉伯语作为第一语言的人…

MySQL数据库,创建和管理表

创建数据库&#xff1a; 方式一&#xff1a;创建数据库 CREATE DATABASE 数据库名&#xff1b;&#xff08;使用的是默认的字符集&#xff09; 方式二&#xff1a;创建数据库并指定字符集 CREATE DATABASE 数据库名 CHARACTER SET 字符集&#xff1b; 方式三&#xff1a;判断数…