排序算法总结

1 排序算法

image-20231107133459995

1.1 快速排序

1.1.1 算法思想
  • 先取一个随机数,然后和数组的最后一个数交换

  • 进行partition过程,也就是比数组最后一个数小的放在数组左边,大的放在右边,相等的在数组中间,最后把数组的最后一个数也要放到中间位置,然后返回相等的那一批数的最左索引和最右索引。

  • 递归前两个过程

1.1.2 时间复杂度
O (N * logN)
1.1.3 代码实现
public class QuickSort {private static void quickSort(int[] arr) {if (arr == null || arr.length < 2) {return;}process(arr, 0, arr.length - 1);}private static void process(int[] arr, int left, int right) {if (left >= right) {return;}swap(arr, left + (int)(Math.random() * (right - left + 1)), right);int[] partition = partition(arr, left, right);process(arr, left, partition[0] - 1);process(arr, partition[1] + 1, right);}private static int[] partition(int[] arr, int left, int right) {int index = left;int small = left - 1;int big = right;while (index < big) {if (arr[index] == arr[right]) {index++;} else if (arr[index] < arr[right]) {swap(arr, index++, ++small);}else {swap(arr, index, --big);}}swap(arr, right, big);return new int[] {++small, big};}private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}public static void main(String[] args) {int[] arr = {1,2,8,9,5};quickSort(arr);for (int i : arr) {System.out.print(i + " ");}}
}

1.2 堆排序

1.2.1 算法思想
  • 给了一个数组,把数组看成完全二叉树结构,现在开始变成堆
  • 从完全二叉树的最后一个值开始进行heapify过程,也就是把每一个值都要和子节点比较大小,把这个节点为顶的树变成堆结构
  • 变成大根堆堆结构之后,将堆顶元素和数组最后一个元素互换,堆的长度减一的同时要进行heapify操作,把剩下元素的要恢复堆结构
  • 重复第三步操作,每次都取一个最大值出来放到原堆的最后,数组有序
1.2.2 时间复杂度
O (N * logN)
1.2.3 代码实现
public class HeapSort {public static void heapSort(int[] arr) {if (arr == null || arr.length < 2) {return;}int heapSize = arr.length;for (int i = arr.length - 1; i >= 0; i--) {heapify(arr, i, heapSize);}swap(arr, 0, --heapSize);while (heapSize > 0) {heapify(arr, 0, heapSize);swap(arr, 0, --heapSize);}}private static void heapify(int[] arr, int index, int heapSize) {int left = index * 2 + 1;while (left < heapSize) {int largest = left + 1 < heapSize ? (arr[left] < arr[left + 1] ? left + 1 : left) : left;largest = arr[index] < arr[largest] ? largest : index;if (largest == index) {return;}swap(arr, largest, index);index = largest;left = index * 2 + 1;}}private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}public static void main(String[] args) {int[] arr = {1,9,7,3,6,4,8,2,5};heapSort(arr);for (int i : arr) {System.out.print(i + " ");}}
}

1.3 桶排序

不基于比较的排序算法,,分为两类,但是都有约束条件

  • 计数排序:要排序的数都要是0或正整数
  • 基数排序:必须是十进制的数,而且是0或正整数

给的数越多,代价越大。一旦要升级的话,要付出的代价更加显而易见。

要排序的最大的数越大,计数排序需要的空间就越多,要排序比如{9,1,96656412},那么此时就需要辅助数组的长度就要是96656413了,很明显性价比不高,就需要用基数排序,基数排序只用两个辅助数组,而且一个计数器数组长度长度固定为10,一个辅助数组长度和要排序的数组长度相同,浪费的空间小。但是如果要排序的数中最大数越小,那么此时就可以用计数排序。

1.3.1 计数排序
算法思想
  • 给定数组,范围[0,intMax]
  • 定义一个辅助数组,辅助数组的长度就是给定数组的长度
  • 遍历数组,给定数组的值与辅助数组的索引对应,
  • 每当有一个给定数组的值等于辅助数组的索引,那么这个索引上的值加一
  • 遍历完成之后,再遍历辅助数组,
  • 每当辅助数组的值不为0,就把辅助数组的索引覆盖在给定数组中
  • 每覆盖一次,辅助数组的值减一,直到减为0才能遍历下一次
时间复杂度
O (N)
代码实现
public class CountSort {public static void countSort(int[] arr) {if (arr == null || arr.length < 2) {return;}int max = arr[0];for (int i = 1; i < arr.length; i++) {max = Math.max(max, arr[i]);}int[] bucket = new int[max + 1];for (int a : arr) {bucket[a]++;}int index = 0;for (int i = 0; i < bucket.length; i++) {while (bucket[i]-- > 0) {arr[index++] = i;}}}// 写比较器验证public static void comparator(int[] arr) {if (arr == null || arr.length < 2) {return;}Arrays.sort(arr);}public static int[] generateRandomArr(int arrLen) {int[] ans = new int[arrLen];for (int i = 0; i < arrLen; i++) {int num = (int) (Math.random() * 1000 + 1);ans[i] = num;}return ans;}public static void main(String[] args) {int arrLength = 1000;int testTimes = 1000000;for (int i = 0; i < testTimes; i++) {int arrLen = (int) (Math.random() * arrLength + 1);int[] arr1 = generateRandomArr(arrLen);int[] arr2 = new int[arr1.length];for (int j = 0; j < arr1.length; j++) {arr2[j] = arr1[j];}countSort(arr1);comparator(arr2);for (int j = 0; j < arr1.length; j++) {if (arr1[j] != arr2[j]) {System.out.println("Oops!");}}}System.out.println("Finish!");}
}
1.3.2 基数排序
算法思想

算法步骤:

  • 给定数组的最大数有几位就遍历几遍(最外层的遍历),先从个位数字开始遍历

    • 目的是从个位数字开始,先把数组中的每个数的个位数字排序,之后再排十位,以此类推。

    • 第一次遍历

      • 遍历每个数组,遍历的过程中,只看现在遍历的数的个位数字,也就是不管数组有多大,每次看的数字只有[0,9]

      • 定义一个计数器数组,每个数字出现一个,计数器数组的与这个数字相等的索引上的值加一

    • 第二次遍历

      • 遍历计数器数组,数组中的每个数等于前面自己的值和前一个数的值相加
      • 目的是找出比这个数小的数字有多少个
    • 第三次遍历

      • 定义一个辅助数组,长度和给定数组的长度相同
      • 从给定数组的末尾开始往前遍历
      • 看计数器数字中,和给定数组的末尾数字的个为数字相同的数字有几个,也就是找比这个数字小的或者等于有多少个
      • 这个数字减一就是辅助数组的索引,索引上放的数字就是给定数组的现在遍历到的数。
      • 这个理解就相当于队列,小的肯定在前面,相同的先进的先出(第一次遍历中,先遍历的在前面,后遍历的在后面,那么最后一个遍历的肯定在最后面)
    • 第四次遍历

      • 交换辅助数组和给定数组的每一个值
      • 接下来就要结束整个大的个位数的遍历,开始遍历十位上的数
时间复杂度
O(N)
代码实现
public class RadixSort {public static void radixSort(int[] arr) {if (arr == null || arr.length < 2) {return;}radixSort(arr, 0, arr.length - 1, maxBits(arr));}// 求数组中最大的数有几位:1000  --->  4位private static int maxBits(int[] arr) {int max = arr[0];for (int i = 1; i < arr.length; i++) {max = Math.max(max, arr[i]);}int ans = 0;while (max != 0) {max /= 10;ans++;}return ans;}// 基数排序的实现public static void radixSort(int[] arr, int left, int right, int digit) {final int radix = 10;int[] help = new int[right - left + 1];for (int d = 1; d <= digit; d++) {int[] count = new int[radix];int i = 0;int j = 0;for (i = left; i <= right; i++) {j= getDigit(arr[i], d);count[j]++;}for (i = 1; i < radix; i++) {count[i] += count[i - 1];}for (i = right; i >= left; i--) {j = getDigit(arr[i], d);help[count[j] - 1] = arr[i];count[j]--;}for (i = left, j = 0; i <= right; i++, j++) {arr[i] = help[j];}}}// num这个数的digit位的数字:(123,1) ---> 3private static int getDigit(int num, int digit) {return ((num / (int) Math.pow(10, digit - 1)) % 10);}// 对数器测试public static void comparator(int[] arr) {if (arr == null || arr.length < 2) {return;}Arrays.sort(arr);}public static int generateRandomNum(int num) {return (int) (Math.random() * num + 1);}public static int[] generateRandomArr(int arrLen, int num) {int length = (int) (Math.random() * arrLen + 1);int[] ans = new int[length];for (int i = 0; i < length; i++) {ans[i] = generateRandomNum(num);}return ans;}public static void main(String[] args) {int num = 1000;int arrLen = 100;int testTimes = 1000000;for (int i = 0; i < testTimes; i++) {int[] arr1 = generateRandomArr(arrLen, num);int[] arr2 = new int[arr1.length];for (int j = 0; j < arr1.length; j++) {arr2[j] = arr1[j];}radixSort(arr1);comparator(arr2);for (int j = 0; j < arr1.length; j++) {if (arr2[j] != arr1[j]) {System.out.println("Oops!");}}}System.out.println("Finish!");}
}

排序算法总结

1 总结
  1. 不基于比较的排序(桶排序),对样本数据有严格的要求,不易改写
  2. 基于比较的排序,只要规定好两个样本怎么比大小就可以直接复用
  3. 基于比较的排序,时间复杂度的极限是O(N*logN)
  4. 时间复杂度O(N*logN),额外空间复杂度低于O(N)且稳定的基于比较的排序是不存在
  5. 为了绝对的速度选择快排,为了省空间选堆排,为了稳定性选归并
2 常见的坑
  1. 归并排序的额外空间复杂度可以变成O(1),“归并排序内部缓存法”,但是将变得不稳定。(还不如用堆排)
  2. “原地归并排序”不好,会让时间复杂度变成O(N*2)。(不如用插入排序)
  3. 快速排序稳定性改进,论文:“01 stable sort”,但是会对样本数据要求更多。(不如用桶排序)
    • 题目:在整型数组中,请把奇数放在数组左边,偶数放在数组右边,要求所有基数之间原始的相对次序不变,所有偶数之间原始相对次序不变,要求时间复杂度O(N),额外空间复杂度O(1)
3 排序优化
  1. 稳定性的考虑:数据是值传递直接快排,引用传递用归并排序
  2. 充分利用O(N*logN)O(N^2)排序各自的优势:小样本量直接插入排序

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

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

相关文章

【LeetCode刷题-回溯】-- 46.全排列

46.全排列 方法&#xff1a;回溯法 一种通过探索所有可能的候选解来找出所有的解的算法&#xff0c;如果候选解被确认不是一个解&#xff0c;回溯法会通过在上一步进行一些变化抛弃该解&#xff0c;即回溯并且再次尝试 使用一个标记数组表示已经填过的数 class Solution {pu…

【前端】yarn介绍和使用

yarn介绍和使用 一、什么是yarn&#xff1f;二、安装yarn三、yarn用法四、yarn更多用法 一、什么是yarn&#xff1f; yarn是快速、可靠、安全的依赖管理。 yarn官网&#xff1a;https://yarn.nodejs.cn/ Yarn 是代码的包管理器。 它允许你与世界各地的其他开发者使用和共享&am…

如何设置实现本地JumpServer远程访问管理界面

文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机&#xff0c;是符合 4A 规范的专业运维安全审计系统。JumpS…

C语言for循环结构经典练习

文章目录 一、for循环基本知识二、经典例题及解析1.水仙花数2.求规定范围内的完数3.求规定范围内质数4.计算阶乘之和5.计算55555555555555(类型)6.计算112123123412345(类型)7.判断用户输入正整数的位数8.判断某正整数是否为回文数9.九九乘法表10.统计用户输入的字符中&#xf…

PTA 公路村村通

现有村落间道路的统计数据表中&#xff0c;列出了有可能建设成标准公路的若干条道路的成本&#xff0c;求使每个村落都有公路连通所需要的最低成本。 输入格式: 输入数据包括城镇数目正整数N&#xff08;≤1000&#xff09;和候选道路数目M&#xff08;≤3N&#xff09;&…

JVM 之 javac、java、javap 命令详解

目录 一. 前言 二. javac 命令 三. java 命令 四. javap 命令 一. 前言 在日常工作中&#xff0c;我们新建 Java工程&#xff0c;写好代码后&#xff0c;编译和运行几乎都是通过 IDE&#xff08;如idea、eclipse&#xff09;工具完成。但作为 Java开发者还是要了解下 Java虚…

Modbus RTU协议及modbus库函数使用

一、与Modbus TCP的区别 在一般工业场景使用modbus RTU的场景还是更多一些&#xff0c;modbus RTU基于串行协议进行收发数据&#xff0c;包括RS232/485等工业总线协议。 与modbus TCP不同的是RTU没有报文头MBAP字段&#xff0c;但是在尾部增加了两个CRC检验字节&#xff08;CRC…

Android之在RecyclerView列表中实现单选

一、实现效果 单选、可取消选中、列表数据可更新&#xff08;选择状态清空&#xff0c;可重新选择&#xff09; RecyclerView列表单选 二、实现步骤 仅展示部分核心代码&#xff0c;请主要参考适配器的定义 1、Item布局 selected_tip_list_item.xml文件 包含一个TextView和…

Spring Boot集成MyBatis实现多数据源访问的“秘密”

文章目录 为什么需要多数据源&#xff1f;Spring Boot集成MyBatis的基础配置使用多数据源小结 &#x1f389;Spring Boot集成MyBatis实现多数据源访问的“秘密” ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客&#x1f388;该系列文章专栏&…

力扣:178. 分数排名(Python3)

题目&#xff1a; 表: Scores ---------------------- | Column Name | Type | ---------------------- | id | int | | score | decimal | ---------------------- 在 SQL 中&#xff0c;id 是该表的主键。 该表的每一行都包含了一场比赛的分数。Score …

TCP /UDP协议的 socket 调用的过程

在传输层有两个主流的协议 TCP 和 UDP&#xff0c;socket 程序设计也是主要操作这两个协议。这两个协议的区别是什么呢&#xff1f;通常的答案是下面这样的。 TCP 是面向连接的&#xff0c;UDP 是面向无连接的。TCP 提供可靠交付&#xff0c;无差错、不丢失、不重复、并且按序…

Selenium介绍及基本使用方法

Selenium是一个开源、免费、简单、灵活&#xff0c;对Web浏览器支持良好的自动化测试工具&#xff0c;在UI自动化、爬虫等场景下是十分实用的&#xff0c;能够熟练掌握并使用Selenium工具可以大大的提高效率。 Selenium简介 Selenium支持多平台、多浏览器、多语言去实现自动化…

深入理解强化学习——马尔可夫决策过程:动作价值函数

分类目录&#xff1a;《深入理解强化学习》总目录 不同于马尔可夫奖励过程&#xff0c;在马尔可夫决策过程中&#xff0c;由于动作的存在&#xff0c;我们额外定义一个动作价值函数&#xff08;Action-value Function&#xff09;。我们用 Q π ( s , a ) Q^\pi(s, a) Qπ(s,a)…

线程提交线程到线程池,有几种方式,哪一种方式是工作中不能使用的,无法捕捉异常,线程池的拒绝策略,线程池的提交方式

线程池的工作原理 JDK中提交线程到线程池&#xff0c;有几种方式&#xff0c;哪一种方式是工作中不能使用的&#xff0c;无法捕捉异常 两种提交任务的方法 ExecutorService 提供了两种提交任务的方法&#xff1a; execute()&#xff1a;提交不需要返回值的任务 submit()&a…

【C语言】多组输入

C系列文章目录 目录 C系列文章目录 一、什么是多组输入&#xff1f; 二、如何使用多组输入 2.1&#xff0c;试题举例讲解 2.2&#xff0c;错误解法 2.3&#xff0c;我们实现多组输入的思路 2.4&#xff0c;第一种正确的解法 2.5&#xff0c;第二种正确的解法 2.6&…

Python入门教程 | Python3 字典(dict)

Python3 字典 字典是另一种可变容器模型&#xff0c;且可存储任意类型对象。 Python3中的字典是一种无序、可变、可迭代的数据结构&#xff0c;它由键&#xff08;key&#xff09;和对应的值&#xff08;value&#xff09;组成。字典在Python中被视为可变对象&#xff0c;这意…

ES ElasticSearch安装、可视化工具kibana安装

1、安装ES docker run -d --name es9200 -e "discovery.typesingle-node" -p 9200:9200 elasticsearch:7.12.1访问测试&#xff1a; http://域名:9200/ 2、安装kibana对es进行可视化操作 执行命令 docker run -d --name kibana5601 -p 5601:5601 kibana:7.1.12.修…

如何实现在公网下使用navicat图形化工具远程连接本地内网的MariaDB数据库

公网远程连接MariaDB数据库【cpolar内网穿透】 文章目录 公网远程连接MariaDB数据库【cpolar内网穿透】1. 配置MariaDB数据库1.1 安装MariaDB数据库1.2 测试局域网内远程连接 2. 内网穿透2.1 创建隧道映射2.2 测试随机地址公网远程访问3. 配置固定TCP端口地址3.1 保留一个固定的…

Redis深入理解-Socket连接建立流程以及文件事件处理机制

Redis Server 运行原理图 Redis 服务器中 Socket 网络建立以及文件事件模型 一个 redis 单机&#xff0c;可以抗几百上千的并发&#xff0c;这里的并发指的就是同时可以有几百个 client 对这个 redis server 发起请求&#xff0c;都需要去建立网络连接&#xff0c;同时间可能会…