选择排序,插入排序,希尔排序的详解与比较
排序算法
- 需要关注的内容:
- 运行时间,性能
- 额外的内存使用
- 原地排序算法:除了函数调用所需的栈和固定数目的实例变量之外无需额外内存
- 其他排序算法:需要额外内存空间来存储另一份数组副本
- 接受任何可以比较的数据类型(泛型)
排序算法类模板
需要导入
package Sort; import java.util.Scanner; /** *排序算法类的模板 */ public class SortExample { public static void sort(Comparable[] a){ //排序算法实现 } public static boolean less(Comparable v, Comparable w){ //对元素进行比较 return v.compareTo(w)<0; } public static void exch(Comparable[] a,int i,int j){ //交换元素 Comparable t=a[i]; a[i]=a[j]; a[j]=t; } public static void show(Comparable[] a){ //在单行中打印数组 for(int i=0;i<a.length;i++){ System.out.print(a[i]+""); System.out.println(); } } public static boolean isSorted(Comparable[] a){ //测试数组元素是否有序 for(int i=1;i<a.length;i++){ if(less(a[i],a[i-1])){ return false; } } return true; } public static void main(String[] args){ Scanner scanner=new Scanner(System.in); while (scanner.hasNext()){ String line=scanner.nextLine(); String[] a=line.split(" "); sort(a); assert isSorted(a); show(a); } } }
选择排序
不断选择剩余元素之中的最小者(最大者)
找到数组的最小(最大)的元素,与数组第一个元素交换位置,然后再剩下的部分中找到最小(最大)的元素,与数组的第二个元素交换,往复,直到数组末尾
import static Sort.SortExample.*; // 选择排序 public class SelectSort { public static void sort(Comparable[] a){ int N = a.length; for(int i = 0; i < N; i ++){ int min = i; for(int j = i+1; j < N; j++) if(less(a[j], a[min])) // 当 a[j] < a[min]时,为true 所以为从小到大排列 min = j; exch(a, i, min); // 在数组a中交换索引为 i min 的数据 } } }
特点
-
运行时间与输入无关,与数组的有序性无关(对一个有序数组和无序数组,所用时间相同)
-
数据移动最少 N次交换
时间复杂度
N 2 N^2 N2
插入排序
始终保持索引左边有序,新加入的元素不断向左移动,插入到左侧有序部分
import static Sort.SortExample.*; public class insertSort { public static void sort(Comparable[] b){ int N = a.length; for(int i = 0; i < N; i ++){ // 循环时要保证当前索引数据小于左边的数据 否则终止循环 for(int j = i; j > 0 && less(a[j], a[j-1]); j --) exch(a, j, j-1); } } }
特点:
-
运行时间与数组的有序性有关,成正比
-
对于处理部分有序的数组很有效
-
部分有序数组
- 数组中每个元素距离它的最终位置都不远
- 一个有序的大数组接一个小数组
- 数组中只有几个元素的位置不正确
-
时间复杂度
N 2 N^2 N2
希尔排序
经过优化的插入排序
对于大规模的乱序数组普通的插入排序很慢,因为它只会交换相邻的元素,元素只能一点一点地移动
优化后,希尔排序可以交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的小数组排序
步骤:
- 将大数组分成多个小数组,每隔 h-1 个元素为一个小数组
- 对每个小数组进行插入排序
- 缩小h 再进行小数组排序
- 继续缩小 h 直到h = 1 为普通的插入排序,即完成
package Sort; import java.util.Arrays; import java.util.Random; import static Sort.SortExample.*; // 优化后的插入排序 public class ShellSort { public static void sort(Comparable[] b) { int n = a.length; int h = 1; while (h < n / 3) h = 3 * h + 1; System.out.println(Arrays.toString(a)); while (h >= 1) { // 将数组排序 for (int i = h; i < n; i++){ for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) { exch(a, j, j - h); } } h /= 3; // h 变化 当 h变为 1 时, 变为普通插入排序 System.out.println(Arrays.toString(a)); } } }
特点:
- 比前两者都快,数组越大,优势越明显
- 运算时间小于 N^2
- 解决问题时,若系统内置排序函数不可用,首先尝试希尔排序
理解希尔排序:
package Sort; import java.util.Arrays; import java.util.Random; import static Sort.SortExample.*; // 优化后的插入排序 public class ShellSort { public static void sort(Comparable[] b) { Comparable[] a = new Comparable[b.length]; System.arraycopy(b, 0, a, 0, b.length); int n = a.length; int h = 1; while (h < n / 3) h = 3 * h + 1; System.out.println(Arrays.toString(a)); while (h >= 1) { System.out.println("h 等于" + h); // 将数组排序 for (int i = h; i < n; i++){ // i++ 进入下一个小数组的排序 每隔 h-1个元素为一个数组 System.out.println("i 等于" + i); // 将 a[i] 插入到 a[i-h], a[i-2*h], a[i-3*h] 之中 // 先进行判断 a[j-h] 是否大于 a[j] 若不是不交换 for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) { // 保证这个小数组的有序性 System.out.println("要做交换的是:" + a[j - h] + ", " + a[j]); exch(a, j, j - h); System.out.println(Arrays.toString(a)); } } h /= 3; // h 变化 当 h变为 1 时, 变为普通插入排序 System.out.println(Arrays.toString(a)); } if(!isSorted(a)) System.out.println("希尔排序失败"); } public static void main(String[] args) { Comparable[] test2 = new Comparable[10]; Random random = new Random(); for(int i = 0; i < 10; i ++) test2[i] = random.nextInt(100); sort(test2); } }
输出为:
[11, 39, 27, 81, 57, 94, 66, 89, 53, 7] h 等于4 要做交换的是:57, 53 [11, 39, 27, 81, 53, 94, 66, 89, 57, 7] 要做交换的是:94, 7 [11, 39, 27, 81, 53, 7, 66, 89, 57, 94] 要做交换的是:39, 7 [11, 7, 27, 81, 53, 39, 66, 89, 57, 94] [11, 7, 27, 81, 53, 39, 66, 89, 57, 94] h 等于1 要做交换的是:11, 7 [7, 11, 27, 81, 53, 39, 66, 89, 57, 94] 要做交换的是:81, 53 [7, 11, 27, 53, 81, 39, 66, 89, 57, 94] 要做交换的是:81, 39 [7, 11, 27, 53, 39, 81, 66, 89, 57, 94] 要做交换的是:53, 39 [7, 11, 27, 39, 53, 81, 66, 89, 57, 94] 要做交换的是:81, 66 [7, 11, 27, 39, 53, 66, 81, 89, 57, 94] 要做交换的是:89, 57 [7, 11, 27, 39, 53, 66, 81, 57, 89, 94] 要做交换的是:81, 57 [7, 11, 27, 39, 53, 66, 57, 81, 89, 94] 要做交换的是:66, 57 [7, 11, 27, 39, 53, 57, 66, 81, 89, 94] [7, 11, 27, 39, 53, 57, 66, 81, 89, 94]
三种排序算法的比较
数据来源:algo4 官方提供的 algo4-data.zip 数据包
先进行1k数据量的比较:
选择排序,1000个数据,执行时间为:0.012673201 s 插入排序,1000个数据,执行时间为:0.0126063 s 希尔排序,1000个数据,执行时间为:0.0015172 s
进行32k数据量的比较:
选择排序,32000个数据,执行时间为:4.1962712 s 插入排序,32000个数据,执行时间为:2.6067247 s 希尔排序,32000个数据,执行时间为:0.027314501 s
其他有关排序算法的文章:
归并排序详解,与其他排序算法的比较
本文作者:清澈的澈
本文链接:https://www.cnblogs.com/lmc7/articles/17531396.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步