八大排序算法Java实现分析

简介: 选择排序(selection sort)每次找一个最小值。具体实现为每次在未排序数据中找到一个最值,并加到以排序数据首部或尾部。
## 0 概述 排序分为: - 内部排序,数据记录在内存中进行排序 - 外部排序,因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需访问外存 `时间复杂度为最差情况下的复杂度` 八大排序都是内部排序: ![](https://my-img.javaedge.com.cn/javaedge-blog/2024/06/2c0f6ef2d2f68f8553a8905f56a4098a.png) 当n较大,应采用时间复杂度为 O(nlog2n) 的排序方法:快排、堆排和归排 `快速排序:目前基于比较的内部排序中被认为最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短 ` ### 稳定性 在值相等情况下,相对次序保持不变。 ## 1 插入排序—直接插入排序(Straight Insertion Sort) ### 基本思想 将一个记录插入到已排序好的有序表中,从而得到一个新且记录数增1的有序表。即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。 ### 要点 设立哨兵,作为临时存储和判断数组边界之用。 ### 示例 ![](https://my-img.javaedge.com.cn/javaedge-blog/2024/06/39845abe8c21f871988318e19ff048aa.png) 元素相等的,把欲插入的元素放在相等元素的后面。 所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序 所以插入排序稳定。 ### 实现 ```java public class InsertionSort { public static void insertionSort(int[] arr) { if (arr == null || arr.length < 2) { return; } for (int i = 1; i < arr.length; i++) { for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) { swap(arr, j, j + 1); } } } public static void swap(int[] arr, int i, int j) { arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j]; } // for test public static void comparator(int[] arr) { Arrays.sort(arr); } // for test public static int[] generateRandomArray(int maxSize, int maxValue) { int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; for (int i = 0; i < arr.length; i++) { arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); } return arr; } // for test public static int[] copyArray(int[] arr) { if (arr == null) { return null; } int[] res = new int[arr.length]; for (int i = 0; i < arr.length; i++) { res[i] = arr[i]; } return res; } // for test public static boolean isEqual(int[] arr1, int[] arr2) { if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { return false; } if (arr1 == null && arr2 == null) { return true; } if (arr1.length != arr2.length) { return false; } for (int i = 0; i < arr1.length; i++) { if (arr1[i] != arr2[i]) { return false; } } return true; } // for test public static void printArray(int[] arr) { if (arr == null) { return; } for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } // for test public static void main(String[] args) { int testTime = 500000; int maxSize = 100; int maxValue = 100; boolean succeed = true; for (int i = 0; i < testTime; i++) { int[] arr1 = generateRandomArray(maxSize, maxValue); int[] arr2 = copyArray(arr1); insertionSort(arr1); comparator(arr2); if (!isEqual(arr1, arr2)) { succeed = false; break; } } System.out.println(succeed ? "Nice!" : "Fucking fucked!"); int[] arr = generateRandomArray(maxSize, maxValue); printArray(arr); insertionSort(arr); printArray(arr); } } ``` ### 效率 时间复杂度:`O(n^2)` 其他插入排序有二分插入排序,2-路插入排序。 常数项极低,所以样本容量很小时`(数组长度小于6)最快!`,Java 的 Collections.sort即是如此设计 ## 2 插入排序—希尔排序(Shell`s Sort) 1959 年由D.L.Shell 提出,相对直接排序有较大的改进,又叫**缩小增量排序**。 ### 思想 - 先将整个待排序的记录序列`分割`成为若干子序列分别进行直接插入排序 - 待整个序列中的记录“`基本有序`”时 - 再`对全体`记录进行依次直接插入排序 ![](https://my-img.javaedge.com.cn/javaedge-blog/2024/06/705a1afb60890205111ff7f94fee5591.png) ### 实现 简单处理增量序列: `d = {n/2 ,n/4, n/8 .....1}`, n为要排序数的个数 - 先将要排序的一组记录按某个增量d(n/2,n为要排序数的个数)分成若干组子序列 - 每组中记录的`下标相差d`,对每组中元素进行`直接插入排序` - 再用一个较小的增量(d/2)对它进行分组 - 在每组中再进行`直接插入排序` - 继续不断`缩小增量`直至为1,最后使用`直接插入排序`完成排序 ```java /** * 直接插入排序的一般形式 * * @param int dk 缩小增量,如果是直接插入排序,dk=1 * */ void ShellInsertSort(int a[], int n, int dk) { for(int i= dk; i= 1 ){ ShellInsertSort(a, n, dk); dk = dk/2; } } ``` 希尔排序时效分析很难,关键码的比较次数与记录移动次数依赖于增量因子序列d的选取,特定情况下可以准确估算出关键码的比较次数和记录的移动次数。目前还没有人给出选取最好的增量因子序列的方法。 增量因子序列可以有各种取法,有取奇数的,也有取质数的,但需要注意:增量因子中除1 外没有公因子,且最后一个增量因子必须为1。希尔排序方法是一个不稳定的排序方法。 ## 3 选择排序—简单选择排序(Simple Selection Sort) ### 思想 在要排序的一组数中,选出最小/大的一个数与第1个位置的数交换 然后在剩下的数当中再找最小/大的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止 ![](https://my-img.javaedge.com.cn/javaedge-blog/2024/06/1298129a37a517060bde931edd8fbff9.png) #### 操作 第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换; 第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换; 以此类推..... 第i 趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换, 直到整个序列按关键码有序。 ### 实现 ```java /** * 数组的最小值 * * @return int 数组的键值 */ int SelectMinKey(int a[], int n, int i) { int k = i; for(int j=i+1 ;j< n; ++j) { if(a[k] > a[j]) k = j; } return k; } /** * 选择排序 * */ void selectSort(int a[], int n){ int key, tmp; for(int i = 0; i< n; ++i) { key = SelectMinKey(a, n,i); //选择最小的元素 if(key != i){ tmp = a[i]; a[i] = a[key]; a[key] = tmp; //最小元素与第i位置元素互换 } print(a, n , i); } } ``` 简单选择排序的改进——二元选择排序 简单选择排序,每趟循环只能确定一个元素排序后的定位 我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。具体实现如下: 无稳定性!!! ## 4 选择排序—堆排序(Heap Sort) 树形选择排序,是对直接选择排序的有效改进 ### 思想 堆的定义:具有n个元素的序列(k1,k2,...,kn),当且仅当满足: ![](https://my-img.javaedge.com.cn/javaedge-blog/2024/06/80aa16394a3c8b4d8ca730973364e4cd.png) 可见,**堆顶元素**(即第一个元素)必为最小项(小顶堆) 若以一维数组存储一个堆,则堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其孩子的值,根结点(堆顶元素)的值是最小(或最大)的 如: (a)大顶堆序列:(96, 83,27,38,11,09) (b)小顶堆序列:(12,36,24,85,47,30,53,91) ![](https://my-img.javaedge.com.cn/javaedge-blog/2024/06/d93b3ca3f055de6bb655567492bc7e11.png) - 初始时把要排序的n个数的序列看作是一棵**顺序存储的二叉树(一维数组存储二叉树)** 调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者最大) - 然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元素 - 依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。称这个过程为**堆排序**。 因此,实现堆排序需解决`两个问题`: 1\. 如何将n 个待排序的数`建堆` 2\. 输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个`新堆` 首先讨论第二个问题:输出堆顶元素后,对剩余n-1元素重新建成堆的调整过程 `调整小顶堆` 1. 设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素 将堆底元素送入堆顶((最后一个元素与堆顶进行交换),堆被破坏(根结点不满足堆的性质) 2. 将根结点与左、右子树中较小元素的进行交换 3. 若与左子树交换:如果左子树堆被破坏,即左子树的根结点不满足堆的性质,则重复方法2. 4. 若与右子树交换,如果右子树堆被破坏,即右子树的根结点不满足堆的性质。则重复方法2. 5. 继续对不满足堆性质的子树进行上述交换操作,直到叶结点,堆被建成 称这个自根结点到叶子结点的调整过程为`筛选` ![](https://ucc.alicdn.com/images/user-upload-01/img_convert/bb0eaa0573011a198456227cc02f601d.png) 再讨论对n 个元素初始`建堆`的过程 建堆方法:对初始序列建堆的过程,就是一个反复进行筛选的过程。 1)n 个结点的完全二叉树,则最后一个结点是第`n / 2`个结点的子树 2)筛选从第`n / 2`个结点为根的子树开始,该子树成为堆。 3)之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。 如图建堆初始过程:无序序列:(49,38,65,97,76,13,27,49) ![](https://ucc.alicdn.com/images/user-upload-01/img_convert/55ad03deed681eed8d45f08476ee0b32.png) ![](https://ucc.alicdn.com/images/user-upload-01/img_convert/33a500463204bbf6ce550ffd4afecd55.png) - 算法的实现 ```java package com.sss; import java.util.Arrays; /** * @author JavaEdge */ public class HeapSort { public static void heapSort(int[] arr) { if (arr == null || arr.length < 2) { return; } for (int i = 0; i < arr.length; i++) { heapInsert(arr, i); } int size = arr.length; swap(arr, 0, --size); while (size > 0) { heapify(arr, 0, size); swap(arr, 0, --size); } } public static void heapInsert(int[] arr, int index) { while (arr[index] > arr[(index - 1) / 2]) { swap(arr, index, (index - 1) / 2); index = (index - 1) / 2; } } 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; } // for test public static void comparator(int[] arr) { Arrays.sort(arr); } // for test public static int[] generateRandomArray(int maxSize, int maxValue) { int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; for (int i = 0; i < arr.length; i++) { arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); } return arr; } // for test public static int[] copyArray(int[] arr) { if (arr == null) { return null; } int[] res = new int[arr.length]; for (int i = 0; i < arr.length; i++) { res[i] = arr[i]; } return res; } // for test public static boolean isEqual(int[] arr1, int[] arr2) { if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { return false; } if (arr1 == null && arr2 == null) { return true; } if (arr1.length != arr2.length) { return false; } for (int i = 0; i < arr1.length; i++) { if (arr1[i] != arr2[i]) { return false; } } return true; } // for test public static void printArray(int[] arr) { if (arr == null) { return; } for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } // for test public static void main(String[] args) { int testTime = 500000; int maxSize = 100; int maxValue = 100; boolean succeed = true; for (int i = 0; i < testTime; i++) { int[] arr1 = generateRandomArray(maxSize, maxValue); int[] arr2 = copyArray(arr1); heapSort(arr1); comparator(arr2); if (!isEqual(arr1, arr2)) { succeed = false; break; } } System.out.println(succeed ? "Nice!" : "Fucking fucked!"); int[] arr = generateRandomArray(maxSize, maxValue); printArray(arr); heapSort(arr); printArray(arr); } } ``` 从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置 所以堆排序有两个函数组成 - 一是建堆的渗透函数 - 二是反复调用渗透函数实现排序的函数 ```cpp void print(int a[], int n){ for(int j= 0; j
目录
相关文章
|
2天前
|
存储 算法 安全
探究‘公司禁用 U 盘’背后的哈希表算法与 Java 实现
在数字化办公时代,信息安全至关重要。许多公司采取“禁用U盘”策略,利用哈希表算法高效管理外接设备的接入权限。哈希表通过哈希函数将设备标识映射到数组索引,快速判断U盘是否授权。例如,公司预先将允许的U盘标识存入哈希表,新设备接入时迅速验证,未授权则禁止传输并报警。这有效防止恶意软件和数据泄露,保障企业信息安全。 代码示例展示了如何用Java实现简单的哈希表,模拟公司U盘管控场景。哈希表不仅用于设备管理,还在文件索引、用户权限等多方面助力信息安全防线的构建,为企业数字化进程保驾护航。
|
11天前
|
监控 算法 网络协议
Java 实现局域网电脑屏幕监控算法揭秘
在数字化办公环境中,局域网电脑屏幕监控至关重要。本文介绍用Java实现这一功能的算法,涵盖图像采集、数据传输和监控端显示三个关键环节。通过Java的AWT/Swing库和Robot类抓取屏幕图像,使用Socket进行TCP/IP通信传输图像数据,并利用ImageIO类在监控端展示图像。整个过程确保高效、实时和准确,为提升数字化管理提供了技术基础。
46 15
|
3月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
106 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
3月前
|
存储 Java
【编程基础知识】 分析学生成绩:用Java二维数组存储与输出
本文介绍如何使用Java二维数组存储和处理多个学生的各科成绩,包括成绩的输入、存储及格式化输出,适合初学者实践Java基础知识。
101 1
|
3天前
|
运维 监控 算法
企业局域网监控软件中 Java 优先队列算法的核心优势
企业局域网监控软件是数字化时代企业网络安全与高效运营的基石,犹如一位洞察秋毫的卫士。通过Java实现的优先队列算法,它能依据事件优先级排序,确保关键网络事件如异常流量、数据泄露等被优先处理,保障系统稳定与安全。代码示例展示了如何定义网络事件类并使用PriorityQueue处理高优先级事件,尤其在面对疑似风险时迅速启动应急措施。这一核心技术助力企业在复杂网络环境中稳健前行,护航业务腾飞。
49 32
|
1天前
|
存储 监控 算法
探秘局域网桌面监控:深入剖析 Java 语言核心算法
在数字化办公时代,局域网桌面监控如同企业的“智慧鹰眼”,确保工作效率与数据安全。本文以Java为载体,揭示哈希表在监控中的关键应用。通过高效的数据结构和算法,哈希表能快速索引设备连接信息,大幅提升监控的时效性和响应速度。代码示例展示了如何用Java实现设备网络连接监控,结合未来技术如AI、大数据,展望更智能的监控体系,助力企业在数字化浪潮中稳健前行。
|
8天前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
17天前
|
缓存 算法 搜索推荐
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
27 6
|
2月前
|
监控 算法 Java
jvm-48-java 变更导致压测应用性能下降,如何分析定位原因?
【11月更文挑战第17天】当JVM相关变更导致压测应用性能下降时,可通过检查变更内容(如JVM参数、Java版本、代码变更)、收集性能监控数据(使用JVM监控工具、应用性能监控工具、系统资源监控)、分析垃圾回收情况(GC日志分析、内存泄漏检查)、分析线程和锁(线程状态分析、锁竞争分析)及分析代码执行路径(使用代码性能分析工具、代码审查)等步骤来定位和解决问题。
|
2月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
71 1