【数据结构】排序 —— 归并排序(mergeSort)、计数排序、基数排序

server/2024/9/23 10:23:05/

Hi~!这里是奋斗的明志,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
🌱🌱个人主页:奋斗的明志
🌱🌱所属专栏:数据结构、LeetCode专栏

在这里插入图片描述

📚本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。

在这里插入图片描述

目录

  • 一、归并排序
    • 1.分治法
    • 2.归并排序基本思想
    • 3.动图演示
    • 4.算法步骤
    • 5.递归思路
    • 6.非递归思路
  • 二、非基于比较的排序
    • 1.计数排序
    • 2.基数排序
      • 2.1 图解
      • 2.2 动图演示
    • 3.代码展示
  • 总结


一、归并排序

1.分治法

归并排序(Merge Sort)是用分治策略(分治法)实现对n个元素进行排序的一种高速的、稳定的算法>排序算法

在介绍归并排序之前,我们首先简单的认识一下分治法

分治法

基本思想:
将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且原问题相同。递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
精髓:
分——将问题分解为规模更小的子问题。
治——将这些规模更小的子问题逐个击破。
合——将已解决的子问题合并,最终得到原问题的解。

2.归并排序基本思想

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的算法>排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使 子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:


在这里插入图片描述


3.动图演示

在这里插入图片描述

4.算法步骤

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

  4. 重复步骤 3 直到某一指针达到序列尾;

  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

5.递归思路

代码如下(示例):

/*** 归并排序** @param array*/public static void mergeSort(int[] array) {//首先要进行分解mergeFunc(array, 0, array.length - 1);}private static void mergeFunc(int[] array, int left, int right) {//递归结束时的判断条件if (left >= right) {//需要考虑什么时候 left > rightreturn;}//找到中间点int mid = left + ((right - left) >> 1);//分界左边mergeFunc(array, left, mid);//分解右边mergeFunc(array, mid + 1, right);//进行合并//封装成一个方法merge(array, left, mid, right);}private static void merge(int[] array, int left, int mid, int right) {//定义四个变量进行理解int s1 = left;int e1 = mid;int s2 = mid + 1;int e2 = right;int k = 0;//申请一个额外的数组空间int[] tmpArray = new int[right - left + 1];//首先要保证两个原本的两段内容不为空,才能持续比较while (s1 <= e1 && s2 <= e2) {if (array[s1] >= array[s2]) {tmpArray[k++] = array[s2++];} else {tmpArray[k++] = array[s1++];}}//看哪个部分还有数据,拷贝到临时数组while (s1 <= e1) {tmpArray[k++] = array[s1++];}while (s2 <= e2) {tmpArray[k++] = array[s2++];}//再次拷贝到原数组for (int i = 0; i < k; i++) {array[i + left] = tmpArray[i];}}

6.非递归思路

    /*** 归并排序非递归** @param array*/public static void mergeSortNor(int[] array) {//首先定义每组排序的个数int gap = 1;while (gap < array.length) {//当gap = array.length时,说明这组数据有序for (int i = 0; i < array.length - 1; i = i + 2 * gap) {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;}//进行归并merge(array,left,mid,right);}//个数每次多2gap *= 2;}}

二、非基于比较的排序

1.计数排序

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。

使用场景,如果给的数据集中到某一个范围的时候,建议使用计数排序,但是空间复杂度较高


用空间来换取时间

  • 首先申请一个额外数组,从头到尾遍历原数组
  • 该数字出现几次,进行统计,额外数组里面的值就++
  • 最后遍历额外数组,进行排序

在这里插入图片描述


【注意点】

假设给 90 ~ 99 之间的数据呢?
利用哈希的思想


代码如下(示例):

public static void countSort(int[] array){//首先要求该数组的最值//先假设int min = array[0];int max = array[0];for (int i = 0; i < array.length; i++) {if (min > array[i]){min = array[i];}if (max < array[i]){max = array[i];}}//创建一个计数数组int[] countArray = new int[max - min + 1];for (int i = 0; i < countArray.length; i++) {int index = array[i] - min;countArray[index]++;}//遍历计数数组//定义一个 k 记录array数组的下标int k = 0;for (int i = 0; i < countArray.length; i++) {while (countArray[i] != 0){array[k] = i + min;k++;countArray[i]--;}}}
public static void main(String[] args) {int[] array = {1,9,5,6,8,8,6,5,4,3,10};Sort.countSort(array);System.out.println(Arrays.toString(array));}

在这里插入图片描述


【总结】

  1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
  2. 时间复杂度: O(MAX(N,范围))
  3. 空间复杂度: O(范围)
  4. 稳定性:稳定

2.基数排序

2.1 图解

【首先按每个数的个位数进行存放】

在这里插入图片描述

【再依次按照顺序出,如果一个框里面有多个数据,按照先进先出的原则】

在这里插入图片描述
【再按每个数的十位数进行存放】
在这里插入图片描述
【再依次按照顺序出,如果一个框里面有多个数据,按照先进先出的原则】

在这里插入图片描述

【再按每个数的百位数进行存放】
在这里插入图片描述

【最后出的时候是有序的】

在这里插入图片描述


2.2 动图演示

在这里插入图片描述

3.代码展示

/*** 基数排序*/
public class RadixSort implements IArraySort {@Overridepublic int[] sort(int[] sourceArray) throws Exception {// 对 arr 进行拷贝,不改变参数内容int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);int maxDigit = getMaxDigit(arr);return radixSort(arr, maxDigit);}/*** 获取最高位数*/private int getMaxDigit(int[] arr) {int maxValue = getMaxValue(arr);return getNumLenght(maxValue);}private int getMaxValue(int[] arr) {int maxValue = arr[0];for (int value : arr) {if (maxValue < value) {maxValue = value;}}return maxValue;}protected int getNumLenght(long num) {if (num == 0) {return 1;}int lenght = 0;for (long temp = num; temp != 0; temp /= 10) {lenght++;}return lenght;}private int[] radixSort(int[] arr, int maxDigit) {int mod = 10;int dev = 1;for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {// 考虑负数的情况,这里扩展一倍队列数,其中 [0-9]对应负数,[10-19]对应正数 (bucket + 10)int[][] counter = new int[mod * 2][0];for (int j = 0; j < arr.length; j++) {int bucket = ((arr[j] % mod) / dev) + mod;counter[bucket] = arrayAppend(counter[bucket], arr[j]);}int pos = 0;for (int[] bucket : counter) {for (int value : bucket) {arr[pos++] = value;}}}return arr;}/*** 自动扩容,并保存数据** @param arr* @param value*/private int[] arrayAppend(int[] arr, int value) {arr = Arrays.copyOf(arr, arr.length + 1);arr[arr.length - 1] = value;return arr;}
}

总结

海量数据的排序问题
外部排序:排序过程需要在磁盘等外部存储进行的排序 前提:内存只有 1G,需要排序的数据有 100G
因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序

  1. 先把文件切分成 200 份,每个 512 M
  2. 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以
  3. 进行 2路归并,同时对 200 份有序文件做归并过程,最终结果就有序了

在这里插入图片描述

在这里插入图片描述


http://www.ppmy.cn/server/99306.html

相关文章

考研数一|极限的计算(笔记)

极限的概念 无限接近但是不等于 函数的极限 1. 在 x x 0 xx_{0} xx0​的极限 设函数 f ( x ) f(x) f(x)在 x 0 x_{0} x0​的某一去心邻域内有定义&#xff0c;如果存在常数 A A A&#xff0c;对于 ∀ ε > 0 \forall\varepsilon>0 ∀ε>0&#xff0c;总 ∃ δ &g…

【网络安全】本地文件包含及远程文件包含漏洞详解

一、文件包含漏洞概述 1.1 什么是文件包含 开发人员将需要重复调用的函数写入一个文件&#xff0c;对该文件进行包含时产生的操作。这样编写代码能减少冗余&#xff0c;降低代码后期维护难度。 保证网站整体风格统一&#xff1a;导航栏、底部footer栏等&#xff0c;把这些不…

docker容器常用指令,dockerfile

docker&#xff1a;容器&#xff0c;主要是解决环境迁移的问题&#xff0c;将环境放入docker中&#xff0c;打包成镜像。 docker的基本组成&#xff1a;镜像(image)&#xff0c;容器(container)&#xff0c;仓库(repository)。镜像相当于类&#xff0c;容器相当于类的实例对象…

直击Vue2/3watch的底层逻辑,字符串长度对侦听效率的影响

目录 直击Vue2/3watch的底层逻辑&#xff0c;字符串长度对侦听效率的影响 一、Vue 2的底层原理 二、Vue 3的底层原理 三、基础类型性能消耗 四、数据变化比较原理 1、Vue 2 中的引用类型比较 2、Vue 3 中的引用类型比较 3、字符串比较&#xff08;基础类型比较&#xf…

qt-05停靠窗口QDockWidget

停靠窗口QDockWidget mainwindow.cpp二级目录三级目录 mainwindow.cpp #include "mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {setWindowTitle(QObject::tr("DOckwindows"));QTextEdit *te new QTextEdit(this);//定义…

[Qt][QWidget]详细讲解

目录 1.概述2.QWidget核心属性1.简介2.核心属性概览 3.QWidget常用属性1.enabled2.geometry1.是什么&#xff1f;2.Window Frame的影响3.相关API4.注意 3.windowTitile4.windowIcon5.windowOpacity6.cursor8.font9.toolTip10.focusPolicy11.styleSheet 1.概述 Widget是Qt中的核…

SpringBoot加载dll文件示例

1、将动态库放在resource文件目录下 2、编写相关加载逻辑 import lombok.extern.slf4j.Slf4j; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.util.HashMap;/*** Description: 加载动态库 .dll文件* author: Be.insighted* c…

Maven学习笔记

Maven 一、Maven简介 1. 什么是Maven ​ maven [ˈmeɪvn] 专家、内行 ​ Apache Maven 是一个软件项目管理和构建工具&#xff0c;可以帮助创建和管理项目 ​ 基于项目对象模型&#xff08;POM&#xff1a;Project Object Model&#xff09;的概念&#xff0c;帮助开发者…