Java数据结构(十一)——归并排序、计数排序

文章目录

  • 归并排序
    • 算法介绍
    • 代码实现
    • 非递归实现
    • 复杂度和稳定性
  • 计数排序
    • 算法介绍
    • 代码实现
    • 复杂度和稳定性

归并排序

算法介绍

归并排序是一种分而治之的排序算法。基本思想是: 将一个数组分成两半,对每半部分递归地应用归并排序先进行分解,然后将排序好的两半合并在一起。

相比于快速排序,归并排序每次都是从中间位置将序列分解为两个子序列,这使得归并排序不会出现像快速排序那样的最坏时间复杂度。

分解成什么程度开始合并?

当某次递归分解得到的左右子区间都有序时进行合并。

如果归并之前左右子区间无序,怎么办?

我们可以从更小的不能再被分割的子区间(左右区间都是一个元素)开始归并。所以我们可以采用递归的方式来实现这样的一个思路。其思路类似于二叉树的后序遍历。

合并的思路是怎样的?

假设现在有一个数组,该数组的前半部分有序,后半部分有序(模拟归并排序的场景),将它们合并成一个有序的序列,思考在原数组进行合并吗?不行,会出现数据覆盖的情况,所以我们得需要一块额外的空间来将合并后的序列存放在这里,合并完毕后,需要将额外空间中的数据拷贝回原数组。

合并的思路:创建两个指针分别指向两个有序区间的开始位置,然后依次比较取小的放到新的数组里,当一个有序区间的所有元素全部放到新数组里后,再将另外一个有序区间剩下的所有元素放到新数组中。


图示:

在这里插入图片描述

归并排序属于易分难合的排序,上面演示的就是每次合并的思路。整体的归并排序可以使用下图表示:

在这里插入图片描述

代码实现

    public void mergeSort(int[] array, int left, int right) {//分组if(left >= right) {return;}int mid = (left + right) >> 1;mergeSort(array, left, mid);mergeSort(array, mid + 1, right);//合并int[] tmp = new int[right - left + 1];int begin1 = left;int end1 = mid;int begin2 = mid + 1;int end2 = right;int index = 0;while(begin1 <= end1 && begin2 <= end2) {if(array[begin1] < array[begin2]) {tmp[index++] = array[begin1++];}else {tmp[index++] = array[begin2++];}}while(begin1 <= end1) {tmp[index++] = array[begin1++];}while(begin2 <= end2) {tmp[index++] = array[begin2++];}//拷贝for(int i = 0; i < tmp.length; i++) {array[i+left] = tmp[i];}
  • 注意问题:拷贝时拷回原数组的位置,不一定是从0下标位置开始拷回。

非递归实现

怎么分解、合并?

最开始将每一个元素看作有序序列,即每次将两个元素个数(gap)为1的序列合并,合并后如下:

在这里插入图片描述

第一次合并后,每两个元素是一个元素个数(gap)为2的有序序列,继续合并:

在这里插入图片描述

确定leftmidright的规律:

在这里插入图片描述

  • left = i;
  • mid = left + gap - 1;
  • right = mid + gap;

以此类推:

在这里插入图片描述

  • 观察上图可以发现:按照上面推导的公式计算leftmidright,可能会导致 rightmid越界,这需要我们特殊处理,如果还是不理解,可以结合接下来的代码实现理解。

    public void mergeSortNoR(int[] array, int left, int right) {int gap = 1;while(gap < array.length) {for (int i = 0; i < array.length; i = i + 2 * gap) {//创建_left、_mid、_right三个变量方便理解,读者实现时可以优化掉。int _left = i;int _mid = _left + gap - 1;//_mid可能越界if(_mid >= array.length) {_mid = array.length - 1;}int _right = _mid + gap;//_right可能越界if(_right >= array.length) {_right = array.length - 1;}//合并int begin1 = _left;int end1 = _mid;int begin2 = _mid + 1;int end2 = _right;int index = 0;int[] tmp = new int[end2 - begin1 + 1];while(begin1 <= end1 && begin2 <= end2) {if(array[begin1] < array[begin2]) {tmp[index++] = array[begin1++];}else {tmp[index++] = array[begin2++];}}while(begin1 <= end1) {tmp[index++] = array[begin1++];}while(begin2 <= end2) {tmp[index++] = array[begin2++];}//拷贝for(int j = 0; j < tmp.length; j++) {array[j+_left] = tmp[j];}}gap *= 2;}}
  • 注意for循环的i变化规则i = i + 2 * gap,这样就能找到当前gap的下一组合并的开始位置,即left

复杂度和稳定性

时间复杂度O(N*log2N)

  • 最优情况:O(N*log2N)
  • 平均情况:O(N*log2N)
  • 最差情况:O(N*log2N)

归并排序的时间复杂度在所有情况下都是O(N*log2N),这是因为它总是将数组分成两半进行递归排序,然后将它们合并。合并操作的时间复杂度是线性的,即O(N),但由于这个过程需要递归地发生log2N次(因为每次数组大小减半),所以总的时间复杂度是O(N*log2N)

空间复杂度O(N)

归并排序在合并过程中需要额外的存储空间来存储临时数组,因此其空间复杂度是O(N)。在最坏的情况下,它需要与原始数组同样大小的额外空间来进行合并操作。

稳定性稳定

对于归并排序还有一些补充的知识:

归并排序又被称为外排序,表明了归并排序能用来对外存数据排序,如硬盘。一般的电脑的内存大小只有4~8G,如果这时候要对硬盘里的10个G的数据进行排序,我们只能依赖归并排序,假设这时候内存只能使用1G,思路是:

先将10个G的数据分为10块1G的数据,一块一块地置入内存(读文件),利用快速排序将10块1G的数据排好序,写入文件,然后利用归并的思想归并完所有的数据。

为什么这种情况只能使用归并排序?补充一点原因

文件的读和写只能依次进行读写,归并排序满足这样的特点。而快速排序需要分别从头和尾部向中间遍历,这样的思想与读写文件的固有特点相违背。


计数排序

算法介绍

计数排序与之前的排序算法不同,它是一种 非基于比较 的排序算法。它特别适用于待排序元素为整数且范围较小的情况,能够在这些情况下实现高效的排序。以下是计数排序的基本原理

计数排序通过统计每个元素出现的次数,然后利用这些次数信息将原始序列重新组合成有序序列。具体来说,它首先确定待排序元素的范围,然后创建一个计数数组(或称为桶),该数组的长度等于待排序元素的最大值加1。接下来,遍历待排序数组,统计每个元素出现的次数,并将这些次数存储在计数数组的相应位置上。最后,根据计数数组的信息,依次将元素放回原始数组中的正确位置,完成排序。

在这里插入图片描述

考虑:如果要排序的数组中的元素只出现了例如100、97、78、99这样的较大数,按照上面的思想,计数数组大小为最大值100+1 = 101,此时计数数组中0~77下标位置的空间全部都浪费了,为了减少空间浪费并提高排序效率,将计数数组大小的计算优化为:length = maxVal - minVal + 1

但这同时意味着优化前的填充计数数组的规则不适用了,新的填充规则为:count[array[i] - minVal]++,具体如下图:

在这里插入图片描述


代码实现

    public void countSort() {//寻找最值,确定范围int minVal = array[0];int maxVal = array[0];for(int i = 1; i < array.length; i++) {if(array[i] < minVal) {minVal = array[i];}if(array[i] > maxVal) {maxVal = array[i];}}int[] count = new int[maxVal - minVal + 1];//计数for (int i = 0; i < array.length; i++) {int tmp = array[i];count[tmp - minVal]++;}//填充int index = 0;for(int i = 0; i < count.length; i++) {while(count[i] > 0) {array[index] = i + minVal;index++;count[i]--;}}}

复杂度和稳定性

时间复杂度O(N+k)

其中N是输入数组的长度,k是输入数组中的最大值与最小值之差(即输入数组的范围)。

注意,如果k远小于N(即输入元素范围远小于元素数量),则时间复杂度接近线性O(N)。然而,如果k与N接近或更大,则时间复杂度可能会变得不那么理想。

空间复杂度O(k),其中k是输入数组的范围。这是因为计数排序需要一个大小为 k+1 的计数数组来存储每个元素的频率。

稳定性稳定,这是因为计数排序按照元素的值将它们放入输出数组的相应位置,而不会改变具有相同值的元素的相对顺序。


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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Linux基础---11优化系统

一.优化SSH连接速度 1&#xff09;修改配置文件 cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak#备份vi /etc/ssh/sshd_config将79行和115行的yes修改为no,最后:wq保存退出&#xff08;79gg和115gg可直接跳至本行&#xff09; 79 行&#xff1a;GSSAPIAuthentication no…

fiddler抓包02_安装

① 访问官网&#xff1a;https://www.telerik.com/fiddler ② 点击“try for free”&#xff0c;选择经典版。 ③ 选择任意用途&#xff0c;输入邮箱&#xff0c;选择地区china&#xff0c;确定下载。 ④ 双击安装包进行安装。 安装后为英文界面&#xff1a;

iOS 18 新功能:控制中心大變身!控制項目自由選配

蘋果於 Apple iOS 18 中為控制中心帶來大改變&#xff0c;變得更具有擴充性&#xff0c;而且將支援第三方應用的控制按鈕&#xff0c;中心內的組件大小也可調節。如今 iOS 18 正式上線&#xff0c;我們就可以試試控制中心不同項目自由選配帶來的效果。 組件可在三尺寸之間調整 …

分页 101012

地址拆分&#xff1a; 10-10-12 假设虚拟地址&#xff1a;0x12345678 0001 0010 0011 0100 0101 0110 0111 10000001 0010 00 -> 0x48 (PDE) 11 0100 0101 -> 0x345 (PTE) 0110 0111 1000 -> 0x678 (物理页偏移)

文字loading加载

效果 1. 导入库 import sys from PyQt5.QtCore import QTimer, Qt, QThread, pyqtSignal from PyQt5.QtGui import QPainter, QFont, QColor, QBrush from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QProgressBar, QLabel 代码首先导入了P…

【Linux】初识信号与信号产生

目录 一、认识信号 1 .什么是信号 2 .哪些情况会产生信号 3 . 查看信号 4 . 信号处理 二、产生信号 1 .通过终端按键产生信号 2 .调用系统函数向进程发信号 3 . 由软件条件产生信号 4 . 由硬件异常产生信号 一、认识信号 1 .什么是信号 你在网上买了很多件商品&#xff0c;再…

JS数组筛选

1、筛选大于10的 要求&#xff1a;将数组[2,0,6,1,77,0,52,0,25,7]中大于等于 10的元素选出来&#xff0c;放入新数组 <script>let arr [2, 0, 6, 1, 77, 0, 52, 0, 25, 7]//声明一个空数组&#xff0c;用来接受数据let newarr []//利用for循环依次判断for (let i 0…

alias 后门从入门到应急响应

目录 1. alias 后门介绍 2. alias 后门注入方式 2.1 方式一(以函数的方式执行) 2.2 方式二(执行python脚本) 3.应急响应 3.1 查看所有连接 3.2 通过PID查看异常连接的进程&#xff0c;以及该进程正在执行的命令行命令 3.3 查看别名 3.4 其他情况 3.5 那么检查这些…

基于SSM的社区爱心捐赠管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSSMVueMySQL的社区爱…

【软考】哈密尔顿回路(Hamiltion)

目录 1. 说明2. c代码实例3. 邻接矩阵截图4. 结果截图 1. 说明 1.一个无向连通图G点上的哈密尔顿&#xff08;Hamiltion&#xff09;回路是指从图G上的某个顶点出发&#xff0c;经过图上所有其他顶点一次且仅一次&#xff0c;最后回到该顶点的路径。 2. c代码实例 #include …

系统架构-面向对象

有对象和没对象一样&#xff0c;鉴于今天中秋节 所以明天姐姐我就恢复单身了&#xff0c;忍这几个小时也没关系&#xff0c;一点不重要了

C++——哈希的应用(位图、布隆)

目录 前言 一、位图、布隆是什么&#xff1f; 二、位图 1.面试题 2.位运算 3 位图的应用 三、布隆过滤器 1、代码实现 2、 布隆过滤器的查找 3、 布隆过滤器删除 4、 布隆过滤器优点 5、 布隆过滤器缺陷 总结 前言 我们学习了哈希算法&#xff0c;我们知道存储数据可以构建一…

应对延迟退休:智能AI如何帮我们?

延迟退休已经成为了当下的热门话题。随着我国人口老龄化的加剧&#xff0c;如何合理延长劳动者的职业生涯并保持他们的工作积极性&#xff0c;已经成了社会关注的焦点。这不仅仅是政策的调整&#xff0c;更是对个人生活、职业规划、健康管理等方面的全方位挑战。 许多人对延迟…

音频左右声道数据传输_2024年9月6日

如下为音频数据传输标准I2S总线的基本时序图 I2S slave将I2S master发送来的左右声道的串行数据DATA转变为16bit的并行数据 WS为左右声道选择信号&#xff0c;WS高代表左声道&#xff0c;WS低代表右声道; WS为高和为低都持续18个周期&#xff0c;前面16个周期用来传输数据。 I2…

【Hot100】LeetCode—32. 最长有效括号

目录 1- 思路题目识别动态规划 2- 实现⭐32. 最长有效括号——题解思路 3- ACM 实现 原题链接&#xff1a;32. 最长有效括号 1- 思路 题目识别 识别1 &#xff1a;给定一个字符串 s &#xff0c;求解 s 中的最长有效括号 动态规划 动态规划五部曲 递推公式难如果遇到了 s.…

如何在Linux下升级R版本和RStudio

一、升级R版本 在Linux上&#xff0c;R的安装通常通过包管理器完成。不同的Linux发行版&#xff08;如Ubuntu、Debian、Fedora等&#xff09;可能略有不同。下面以Ubuntu为例&#xff0c;介绍如何升级R版本。如果你使用其他发行版&#xff0c;步骤可能类似。 二.更新步骤 2.…

MySQL —— 视图

概念 视图是一张虚拟的表&#xff0c;它是基于一个或多个基本表或其他视图的查询结果集。 视图本身不存储数据&#xff0c;而是通过执行查询来动态生成数据&#xff0c;用户可以像操作普通表一样使用视图来进行查询更新与管理等操作。 视图本身也不占用物理存储空间&#xf…

NC 排序

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 给定一个长度…

时序约束进阶三:Create_clock与Create_Generated_Clock详解

目录 一、前言 二、生成时钟 2.1 示例设计 2.2 主时钟约束 1&#xff09;约束对象解析 2&#xff09;约束到非时钟位置 2.3 生成时钟约束 1&#xff09;无约束 2&#xff09;倍频约束 3&#xff09;生成时钟的主时钟约束不正确 4&#xff09;使能时钟控制的约束 5&…

格式化u盘选择FAT还是NTFS U盘和硬盘格式化两者选谁

Mac用户在将U盘或硬盘进行格式化时&#xff0c;选择FAT还是NTFS往往是一个让人纠结的问题。很多用户不知道这两个格式之间有什么区别&#xff0c;更不知道在格式化时如何做出选择。本文将为大家介绍Mac选择FAT还是NTFS&#xff0c;并为大家推荐U盘和硬盘格式化两者选谁。 一、…