常见排序算法总结

文章目录

  • 比较排序
    • 冒泡排序
    • 选择排序
    • 插入排序
    • 归并排序
    • 快速排序
    • 堆排序
    • 希尔排序
  • 非比较排序(桶排序)
    • 计数排序
    • 基数排序

比较排序

冒泡排序

嵌套循环,每次内层循环执行时,数组的每两个元素交换,将一个最大/小的数排到数组末尾

在这里插入图片描述

public void bubbleSort(int[] arr){for (int i = 0; i < arr.length; i++) {// 内层循环中,每轮都是让最后一个位置排好序,然后外层循环向前递进for (int j = 0; j < arr.length - i - 1; j++) {if (arr[j] > arr[j+1]){  // 把大的数挪到后面int tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}
}

选择排序

嵌套循环,每次内层循环执行时,子数组的后m-1个元素寻找最小值,再与子数组的首元素比较,如果更大/小就交换

在这里插入图片描述

public void selectSort(int[] arr){for (int i = 0; i < arr.length - 1; i++){    // len个元素只需比较n-1次int min_idx = i;for (int j = i + 1; j < arr.length; j++){if (arr[j] < arr[min_idx])     // 在i+1后面的数两两比较,找到最小下标min_idx = j;}// 内层遍历完之后,我们拿到后面的元素的最小值下标min_id,要和前面的下标i元素进行值的对比if (arr[min_idx] < arr[i]){        // 能篡位就篡位!arr[min_idx] = arr[min_idx] ^ arr[i];arr[i]       = arr[min_idx] ^ arr[i];arr[min_idx] = arr[min_idx] ^ arr[i];}}
}

插入排序

嵌套循环,默认第一个元素不处理,每插入/增加一个数,就依次跟前面的数两两对比,将前面的所有数进行排好序

在这里插入图片描述

public void insertSort(int[] arr){for (int i = 1; i < arr.length; i++) {  // 外层循环len-1次// 对应外层的len-1次,内层分别循环1到len-1次,且后一个数 < 前一个数才会进循环交换for (int j = i - 1; j >= 0 && arr[j + 1] < arr[j]; j--){// 如果后一个元素更小,就交换到前面来arr[j]     = arr[j] ^ arr[j + 1];arr[j + 1] = arr[j] ^ arr[j + 1];arr[j]     = arr[j] ^ arr[j + 1];}}
}

归并排序

递归到最底层时会执行merge排序,回溯时排序保证了此时两个子数组都必然有序

在这里插入图片描述

// 不传参默认对整个数组进行归并排序
public void mergeSort(int[] arr) {if (arr == null || arr.length < 2) return;mergeSort(arr, 0, arr.length - 1);
}
// 传参则对[l, r]区间进行归并排序
public void mergeSort(int[] arr, int l, int r) {if (l == r) return;int mid = l + ((r - l) >> 1);mergeSort(arr, l, mid);mergeSort(arr, mid + 1, r);merge(arr, l, mid, r);  // 回溯位置归并,所以此时的子数组皆已排序完成
}
// 对 arr 的 [l, m]  [m+1, r] 两部分子数组进行归并排序
public void merge(int[] arr, int l, int m, int r) {int[] help = new int[r - l + 1];  // 辅助数组,暂时存放归并后的数组int i = 0;// 两个子数组的起始索引位置 p1 p2int p1 = l, p2 = m + 1;// 精髓:看数据哪边小,就先挪哪边的数据到help数组里while (p1 <= m && p2 <= r) help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];while (p1 <= m) help[i++] = arr[p1++];while (p2 <= r) help[i++] = arr[p2++];// 将暂存的归并后的数组,覆盖到 arr 上,使原数组排好序for (i = 0; i < help.length; i++) {arr[l + i] = help[i];}
}

快速排序

整体的quickSort函数swap后,再进行partition,划分三部分后,<区域 >区域 分别递归调用局部的quicksort

在这里插入图片描述

public void quickSort(int[] arr) {if (arr == null || arr.length < 2) return;quickSort(arr, 0, arr.length - 1);
}public void quickSort(int[] arr, int l, int r) {if (l < r) {// 左神的随机:让最右的r与[l,r-1]之间的随机一个数交换swap(arr, l + (int) (Math.random() * (r - l + 1)), r);// 先partition再进递归(与归并的回溯时merge区分)int[] p = partition(arr, l, r);quickSort(arr, l, p[0] - 1); // = 区域的左部分quickSort(arr, p[1] + 1, r); // = 区域的右部分}
}public int[] partition(int[] arr, int l, int r) {int less = l - 1;int more = r;while (l < more) {if (arr[l] < arr[r]) {swap(arr, ++less, l++);} else if (arr[l] > arr[r]) {swap(arr, --more, l);} else {l++;}}swap(arr, more, r);  // 最右那个 = 划分值挪回原位return new int[] { less + 1, more };  // = 区域:[ less+1, more ]
}public void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}

堆排序

堆是一种比较特殊的完全二叉树

分为:大根堆小根堆

由于完全二叉树的结构性质,可以使用数组或列表等线性数据结构来存储堆(此处用优先级队列)

大根堆:每个子树的最大节点是头结点

小根堆:每个子树的最小节点是头结点

在这里插入图片描述

public static void heapSort(int[] arr) {if (arr == null || arr.length < 2) return;// 1. 先构建大根堆,完成后就已知arr最大值(根节点的value)// 传统 heapInsert1// for (int i = 0; i < arr.length; i++) {//    heapInsert1(arr, i);// }// 进化 heapInsert2heapInsert2(arr);// 2. 取出根节点这个最大值,与末尾节点做交换,然后最大值相当于到末尾排好序了,所以移除这个末尾(最大值)元素//    末尾节点到根节点位置后就heapify,再去重新进行大根堆的构建int size = arr.length;swap(arr, 0, --size);while (size > 0) {heapify(arr, 0, size);swap(arr, 0, --size);}
}
// 1. 构建大根堆   v1.0 传统版
public static void heapInsert1(int[] arr, int index) {while (arr[index] > arr[(index - 1) / 2]) {swap(arr, index, (index - 1) /2);index = (index - 1)/2 ;}
}
// 1. 构建大根堆   v2.0 进化版
public static void heapInsert2(int[] arr){for (int i = arr.length - 1; i >= 0; i--){heapify(arr, i, arr.length);}
}
// 2. 某个数在index位置,能否往下(数组下标越大的方向)移动
public static void heapify(int[] arr, int index, int size) {int left = index * 2 + 1;while (left < size) {int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;largest = arr[largest] > arr[index] ? largest : index;if (largest == index)break;swap(arr, largest, index);index = largest;left = index * 2 + 1;}
}public static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}

希尔排序

间隔分组,且分组间隔依次减半,每次分组后,每个组内排序都是插入排序

相比于一开始就用插入排序,希尔排序的比较次数更少,效率更高

为什么呢?大概是因为插入排序没预处理,极端情况可能让一个很小很右的数一直比比比比比,比到最开头,浪费比较次数

而希尔排序会让每次分组比较后,基本上左侧大区间 < 右侧大区间 类似 log(n) 但实际复杂度为 O ( n 1.3 − 2 ) O(n^{1.3-2}) O(n1.32) -表示范围,不是减号

所以其实最坏情况和插入排序一样,只不过可能性较小,正常都比插入排序好些

public void shellSort(Comparable[] arr) {// 不断减半分组排序for (int gap = arr.length / 2; gap > 0; gap /= 2) {// 对每个步长的整个数组进行插入排序for (int i = gap; i < arr.length; i++) {// 内层就是插入排序,但交换的间隔单位为gap,不会影响其他分组for (int j = i; j >= gap && arr[j].compareTo(arr[j - gap]) < 0; j -= gap) {// 交换元素Comparable temp = arr[j];arr[j] = arr[j - gap];arr[j - gap] = temp;}}}
}

非比较排序(桶排序)

计数排序

省流:词频表

// only for 0~200 value,可以更大,但太占内存空间了,还不如换别的
// 仅适用于数组数据较为集中密集的情况,太稀疏的话,中间一堆内存空间浪费
public void countSort(int[] arr) {if (arr == null || arr.length < 2) return;int max = Integer.MIN_VALUE;for (int i = 0; i < arr.length; i++) {max = Math.max(max, arr[i]);}// 一个词频表int[] bucket = new int[max + 1];for (int i = 0; i < arr.length; i++) {bucket[arr[i]]++;}// 词频表中的数据依次取出放到arr,保证有序int i = 0;for (int j = 0; j < bucket.length; j++) {while (bucket[j]-- > 0) {arr[i++] = j;}}
}

基数排序

数字按最多多少位,先补齐

从个位开始排,开始进桶再出桶,完成个位上的优先级排序

从十位开始排,还是进桶出桶,完成十位上的优先级排序

依次百位,千位… 越往后,越晚排,优先级越高

同时之前的优先级也会保留下来

所有都排完,数组就有序了(升序/降序)

某种情况比计数排序好,因为数字,如果按照普通十进制理解,则只需准备10个不同数字的桶就好了!

局限:得根据多少进制准备多个桶,需要有进制这个前提规则!

前提得知道空间范围,比如人的年龄,正数[0-200],否则内存爆炸!

所以这种不基于比较的算法应用范围很局限,大部分情况下,不如之前的所有比较算法!

图解如下:
在这里插入图片描述

前缀和处理,倒序入桶,以及词频——比较难理解,可以看下面左神讲解

左神桶排序 2:15:00

// 只适合非负数
public void radixSort(int[] arr) {if (arr == null || arr.length < 2) return;radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
// 计算数组中最大元素的位数(比如324就是三位数)
public int maxbits(int[] arr) {int max = Integer.MIN_VALUE;for (int i = 0; i < arr.length; i++) {max = Math.max(max, arr[i]);}int res = 0;while (max != 0) {res++;max /= 10;}return res;
}public void radixSort(int[] arr, int begin, int end, int digit) {final int radix = 10;  // 默认十进制int i = 0, j = 0;// 有多少个数就准备多少个辅助空间int[] bucket = new int[end - begin + 1];// 有多少个“十进制位”,就出桶进桶多少次,所以循环digit次(从个位开始算)for (int d = 1; d <= digit; d++) {int[] count = new int[radix];// 1. 遍历每一个arr元素,根据外层循环次数,取出个/十/百...位上的数字(getDigit的作用)for (i = begin; i <= end; i++) {j = getDigit(arr[i], d);count[j]++;}// 2. 遍历好个/十/百...位上的所有数字后,count数组记录了对于数字出现的频数//    接下来,把词频数换成前缀数,记录<=当前索引的数字个数,处理成如下效果://                        "10个空间"//          count[0] 当前位(d位)是 0      的数字有多少个//          count[1] 当前位(d位)是 0和1   的数字有多少个//          count[2] 当前位(d位)是 0,1和2 的数字有多少个//          count[i] 当前位(d位)是 0-i    的数字有多少个for (i = 1; i < radix; i++) {count[i] = count[i] + count[i - 1];}// 3. 入桶操作:从右往左遍历,根据个/十/百...位上的数字大小进行相应入桶,排好序for (i = end; i >= begin; i--) {j = getDigit(arr[i], d);bucket[count[j] - 1] = arr[i];count[j]--;}// 4. 出桶操作:将bucket(help)辅助数组,覆盖赋值到原来的数组arr中for (i = begin, j = 0; i <= end; i++, j++) {arr[i] = bucket[j];}}
}
// 获取一个整数 x 在指定位数 d 上的数字
public int getDigit(int x, int d) {return ((x / ((int) Math.pow(10, d - 1))) % 10);
}

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

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

相关文章

AIGC代码学习记录

本文主要记录不同的SD模型代码中实现的一些细节&#xff0c;如text2img,img2img,inpaint等等 1. 文生图 第一步&#xff1a;生成随机的latent feature (n,4,64,64)&#xff1b;n为生成的图片个数&#xff1b; 第二步&#xff1a;对于prompt用clip生成特征&#xff0c;正向提示…

PDF-Extract-Kit (PDF内容抽取开源项目)

Github 地址&#xff1a;https://github.com/opendatalab/PDF-Extract-Kit 整体介绍 PDF文档中包含大量知识信息&#xff0c;例如文本、表格、图像、公式等。此外&#xff0c;PDF的文档布局也相当复杂&#xff0c;页眉、页脚、表格标题、图片标题等等&#xff0c;提取高质量的…

python调用chrome浏览器自动化如何选择元素

功能描述&#xff1a;在对话框输入文字&#xff0c;并发送。 注意&#xff1a; # 定位到多行文本输入框并输入内容。在selenium 4版本中&#xff0c;元素定位需要填写父元素和子元素名。 textarea driver.find_element(By.CSS_SELECTOR,textarea.el-textarea__inner) from …

2024.04最新 鹦鹉优化算法 (PO) 开源MATLAB代码

!!!只需要代码的直接跳转到最后一节&#xff0c;有开源代码 1.算法简介2.灵感来源与核心思想2.1 灵感来源2.2 核心思想 3.主要步骤3.1 种群初始化3.2 觅食行为3.3 停留行为3.4 交流行为3.5 对陌生人的恐惧行为 4.伪代码与核心代码4.1流程图与伪代码流程图伪代码 4.2 核心代码(P…

nodejs下载+react安装

一、nodejs安装 1、nodejs下载 具体安装可参考连接&#xff1a;2023最新版Node.js下载安装及环境配置教程&#xff08;非常详细&#xff09;从零基础入门到精通&#xff0c;看完这一篇就够了_nodejs安装及环境配置-CSDN博客 下载地址&#xff1a;Node.js — 下载 Node.js 测…

ubuntu 更新源

前言 实现一键替换在线源 一键更新源 ubuntu 全球镜像站以下支持现有ubuntu 20&#xff0c;22&#xff0c;24 echo "Delete the default source" rm -rf /etc/apt/sources.listecho "Build a new source" cat <<EOF>>/etc/apt/sources.li…

微信小程序 button样式设置为图片的方法

微信小程序 button样式设置为图片的方法 background-image background-size与background-repeat与border:none;是button必须的 <view style" position: relative;"><button class"customer-service-btn" style"background-image: url(./st…

MVC架构在Web开发中的实现

MVC架构在Web开发中的实现 1、MVC架构概述2、MVC架构的实现 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Web开发领域&#xff0c;MVC&#xff08;Model-View-Controller&#xff09;架构模式是一种广泛使用的软件设计模式&#xff0c…

【学习笔记】虚幻SkeletalMesh学习(一)基础介绍

文章目录 零、前言一、资源介绍1.1 骨架资源1.2 骨架网格体资源 二、UE4中的定义2.1 骨骼数据2.2 模型网格数据 三、渲染3.1 RenderData的初始化3.2 渲染对象的创建3.3 渲染对象的更新3.3.1 游戏线程的更新&#xff08;*FSkeletalMeshObjectGPUSkin::Update*&#xff09;3.3.2 …

Android11 framework 禁止三方应用开机自启动

Android11应用自启动限制 大纲 Android11应用自启动限制分析验证猜想&#xff1a;Android11 AOSP是否自带禁止三方应用监听BOOT_COMPLETED​方案禁止执行非系统应用监听到BOOT_COMPLETED​后的代码逻辑在执行启动时判断其启动的广播接收器一棍子打死方案&#xff08;慎用&#…

矩阵形式的bezier曲线

本文分享一段矩阵形式的bezier代码&#xff1a; clc clear% 控制点 P [25;10;5;13]; %% 获得M矩阵 n length(P) - 1; M zeros(n1,n1); for i 1:n1for j 1:n1if(ij<n3)M(i,j) (-1)^(n -i-j2)*nchoosek(n,n-i1)*nchoosek(n-i1,j-1);elseM(i,j) 0;endend end t_temp l…

buu做题(5)

目录 [GXYCTF2019]禁止套娃 方法一: 方法二: [NCTF2019]Fake XML cookbook [GXYCTF2019]禁止套娃 页面里啥也没有 使用dirsearch 扫一下目录 发现有 git 使用工具githack拉取源码 <?php include "flag.php"; echo "flag在哪里呢&#xff1f;<br&g…

PostgreSQL使用(三)

说明&#xff1a;本文介绍PostgreSQL的数据类型和运算符&#xff1b; 数据类型 常用的数据类型如下&#xff1a; 运算符 如下&#xff1a; 运算符的优先级如下&#xff1a; 不需要强记硬背&#xff0c;只需要记住括号可以提高运算符的优先级即可&#xff1b; 总结 本文介绍…

【JavaEE进阶】——Spring AOP

目录 &#x1f6a9;Spring AOP概述 &#x1f6a9;Spring AOP快速⼊⻔ &#x1f393;引入AOP依赖 &#x1f393;编写AOP程序 &#x1f6a9;Spring AOP 详解 &#x1f393;Spring AOP核⼼概念 &#x1f393;通知类型 &#x1f393;PointCut &#x1f393;切⾯优先级 Ord…

【JavaScript 算法】拓扑排序:有向无环图的应用

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、算法原理二、算法实现方法一&#xff1a;Kahn算法方法二&#xff1a;深度优先搜索&#xff08;DFS&#xff09;注释说明&#xff1a; 三、应用场景四、总结 拓扑排序&#xff08;Topological Sorting&#xff09;是一种…

核函数支持向量机(Kernel SVM)

核函数支持向量机&#xff08;Kernel SVM&#xff09;是一种非常强大的分类器&#xff0c;能够在非线性数据集上实现良好的分类效果。以下是关于核函数支持向量机的详细数学模型理论知识推导、实施步骤与参数解读&#xff0c;以及两个多维数据实例&#xff08;一个未优化模型&a…

笔记 3 : 继续彭老师课本第 3 章的 arm 的汇编指令

&#xff08;26&#xff09; 指令 LDR &#xff1a; &#xff08;27&#xff09; STR &#xff1a; 可见&#xff0c;从语法上将&#xff0c; &#xff01; 提示编译器进行更复杂的编译&#xff0c;对应内涵更复杂的指令。 &#xff08;28&#xff09; LDR 与 STR 指令还可…

Android C++系列:Linux信号(三)

可重入函数 不含全局变量和静态变量是可重入函数的一个要素可重入函数见man 7 signal在信号捕捉函数里应使用可重入函数在信号捕捉函数里禁止调用不可重入函数例如:strtok就是一个不可重入函数,因为strtok内部维护了一个内部静态指针,保存上一 次切割到的位置,如果信号的捕捉…

echarts实现3d柱状效果

代码如下&#xff0c;单个的调第一个方法&#xff0c;多个柱状的调第二个方法&#xff0c;具体情况修改参数或者二次开发即可 //3d柱状图 export function getEcharts3DBar (xAxisData:string[][name1,name2,name3], data:number[][1,2,3], colorObj:IBaseObject{topStartColo…

通过albumentation对目标检测进行数据增强(简单直接)

albumentation官方文档看不懂&#xff1f;xml文件不知道如何操作&#xff1f;下面只需要修改部分代码即可上手使用 要使用这个方法之前需要按照albumentation这个库还有一些辅助库,自己看着来安装就行 pip install albumentation pip install opencv-python pip install json…