文章目录

  • 1️⃣必备排序常识
  • 2️⃣插入排序
    • 1.直接插入排序
    • 2.优化后的插入排序
    • 3.折半插入排序
    • 4.性能比较
  • 3️⃣希尔排序
    • 性能比较

1️⃣必备排序常识

稳定性:在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求能在内存和硬盘(外部存储器)之间移动数据的排序。
时间复杂度:一个排序算法在执行过程中所耗费的时间量级的度量。
空间复杂度:一个排序算法在运行过程中临时占用存储空间大小的度量。

本次讲解的排序都是内排序

2️⃣插入排序

1.直接插入排序

排序原理:

将集合分为两个区间,下标i代表当前遍历的未排序的元素,已经排序的区间[0...i),待排序区间[i...n]
每次从待排序区间中间第一个元素“插入”到已排序区间的合适位置,该位置后的已排序元素都往后移动一位,直到整个数组有序。

过程展示:

代码实现:

public static void insertSort(int[] arr){// 已排序区间[0,i)// 待排序区间[i...n]for (int i = 0; i < arr.length - 1; i++) {// 待排序区间的第一个元素arr[i]// 从待排序区间的第一个元素向前看,找到合适的插入位置for (int j = i ; j >= 0 && arr[j + 1] < arr[j]; j--) {swap(arr,j,j + 1);}}
}
private static void swap ( int[] arr, int i, int j){int temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}

特性总结:

  1. 如果当待排序的元素 > 有序区间的最后一个元素时,说明该元素有序区间和待排序元素都有序了,就不需要将该元素再排序,就可以提前结束内层循环。所以元素集合越接近有序,直接插入排序算法的时间效率越高。极端情况下,当集合是一个完全有序的集合,插入排序内层循环一次都不会进入。
    所以插入排序经常作为高阶排序算法的优化手段之一(在小区间上接近有序的时候使用)。
  2. 时间复杂度:O(N) ~ O(N^2)。最好的情况是刚好是升序或降序(即和算法的排序顺序一致的时候),只遍历一遍;最坏的情况为刚好有序的但是和算法的顺序刚好相反,此时时间复杂度为n * (n-1) * ··· *2 * 1为等差数列,按公式计算得为O(N^2)。所以在数据大部分有序的情况下使用插入排序还是不错的。
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定

2.优化后的插入排序

上一种排序方法,在进行排序的时候会不断调用swap方法来将两个元素的位置交换,其实不用每次比较交换,可以直接找到合适的位置,将后面的元素挨个往后移动,将该元素放在位置上集合。
这样排序的运行时间可以大大加快,因为不断调用函数,创建临时变量是一个比较耗时的操作。使用优化后的插入排序可以减少这些操作,后面会给出排序的性能比较。

代码实现:

//不交换元素,直接存放
public static void insertSortOp(int[] arr){for (int i = 0; i < arr.length - 1; i++) {//比较如果不需要插入则直接进入下次循环if(arr[i] > arr[i + 1]){int j = i;//将需要插入的元素保存int tmp = arr[i + 1];//将该位置和后面已经排好序的元素一次往后移动for (; j >= 0 && arr[j] > tmp; j--) {arr[j + 1] = arr[j];}//将该元素放在该位置上arr[j + 1] = tmp;}}
}

3.折半插入排序

因为在插入排序中,每次都是在有序区间中选择插入位置,因此可以使用二分查找来定位元素的插入位置。

代码实现:

//插入排序二分搜索优化版本
public static void insertSortBS(int[] arr){// 有序区间[0..i]// 无序区间(i...n]for (int i = 0; i < arr.length - 1; i++) {if(arr[i + 1] < arr[i]){int left = 0;int right = i;int val = arr[i + 1];while(left <= right){int mid = left + ((right - left) >> 1);if(arr[mid] <= val)left = mid + 1;elseright = mid - 1;}// 搬移left..i的元素for (int j = i; j >= left; j--) {arr[j + 1] = arr[j];}// left就是val插入的位置arr[left] = val;}}
}

4.性能比较

这里给出测试使用的类:

/*** 排序的辅助类* 生成测试数组以及对排序算法进行测试**/
public class SortHelper {// 获取随机数的对象private static final ThreadLocalRandom random = ThreadLocalRandom.current();//在[left...right]上生成n个随机数public static int[] generateRandomArray(int n,int left,int right) {int[] arr = new int[n];for (int i = 0; i < arr.length; i++) {arr[i] = random.nextInt(left,right);}return arr;}/*** 生成一个大小为n的近乎有序的数组* @param n* @param times 交换的次数,次数越小越有序,次数越大越无序* @return*/public static int[] generateSoredArray(int n,int times) {int[] arr = new int[n];for (int i = 0; i < n; i++) {arr[i] = i;}// 交换部分元素,交换次数越小,越有序for (int i = 0; i < times; i++) {// 生成一个在[0..n]上的随机数int a = random.nextInt(n);int b = random.nextInt(n);int temp = arr[a];arr[a] = arr[b];arr[b] = temp;}return arr;}// 根据传入的方法名称就能调用这个方法,需要借助反射// 根据方法名称调用相应的排序方法对arr数组进行排序操作public static void testSort(String sortName,int[] arr) {Class<SevenSort> cls = SevenSort.class;try {Method method = cls.getDeclaredMethod(sortName,int[].class);long start = System.nanoTime();method.invoke(null,arr);long end = System.nanoTime();if (isSorted(arr)) {// 算法正确System.out.println(sortName + "排序结束,共耗时:" + (end - start) / 1000000.0 + "ms");}} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {e.printStackTrace();}}// 生成一个arr的深拷贝数组// 为了测试不同排序算法的性能,需要在相同的数据集上进行测试public static int[] arrCopy(int[] arr) {return Arrays.copyOf(arr,arr.length);}public static boolean isSorted(int[] arr) {for (int i = 0; i < arr.length - 1; i++) {if (arr[i] > arr[i + 1]) {System.err.println("sort error");return false;}}return true;}
}

排序十万个随机数时:

public static void main(String[] args) {int n = 100000;int[] arr = SortHelper.generateRandomArray(n, 0, Integer.MAX_VALUE);int[] arrCopy1 = SortHelper.arrCopy(arr);int[] arrCopy2 = SortHelper.arrCopy(arr);SortHelper.testSort("insertSort", arrCopy);SortHelper.testSort("insertSortOp", arrCopy1);SortHelper.testSort("insertSortBS", arrCopy2);
}

运行结果:

可以看出在十万个随机数下,经过优化的插入排序和二分搜索的性能都有很大提升。

排序十万个接近有序数组时:

int n = 100000;
//将有序的数组元素进行随机交换100次就可构成一个接近有序的数组
int[] arr = SortHelper.generateSoredArray(n,100);

运行结果:

可以看出在十万个接近有序的数组下,插入排序的性能是很不错的,接近O(n)的时间复杂度。

3️⃣希尔排序

排序原理:

希尔排序法(Shell Sort)又称缩小增量法。希尔排序法的基本思想是:先选定一个整数gap,把待排序数据按照距离为gap分成同多组,并对每一组内的数据进行排序。然后,逐渐将gap减小,即每组数据的间距减小,重复上述分组和排序的工作。当gap=1时,此时就是插入排序了,而且经过前面的排序,整个数组已经被调整得接近有序,使用插入排序就可以较快排好序。

过程展示:

代码实现:

public static void shellSort(int[] arr){int gap = arr.length >> 1;//最后一次循环的gap为1,也就是插入排序了while(gap >= 1){shellSortHelper(arr,gap);//减小间距gapgap >>= 1;}
}private static void shellSortHelper(int[] arr, int gap) {//可以看出这个结构就是插入排序,只是比较元素的间隔变化而已for (int i = 0; i < arr.length - gap; i++) {for (int j = i; j >= 0 && arr[j + gap] < arr[j]; j-=gap) {swap(arr,j,j+gap);}}
}

特性总结:

  1. 希尔排序是对直接插入排序的优化。
  2. gap > 1时都是预排序,目的是让数组更接近于有序,不要小看这些预排序,可以让插入排序的性能有很大的提升。当gap == 1时,数组已经接近有序的了,这样排序起来就会很快。这样整体而言,可以达到优化的效果。后面会进行性能的测试。
  3. 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(n*logn)~O(n^2),当间隔gap取得好的条件下可以达到最好情况,最不理想的情况是当gap == 1的时候,也就是直接插入排序。
  4. 稳定性:不稳定。当相同的值被分到不同的组中在进行排序,不同的组中数值大小不确定,此时就不能保证这些数还能保持先后顺序。

性能比较

排序十万个随机数时:

运行结果:

可见此时得益于希尔排序优秀的时间复杂度,快了插入排序将近百倍。希尔排序循环大概进行的次数为:O(n*logn)约等于170万。
而直接插入排序接近最坏的情况,O(n^2)后的计算结果为100亿次。由此可见两种排序在这种情况下根本不是一个数量级的。

排序十万个接近有序数组时:

运行结果:

在接近有序的数组,插入排序接近O(n)的时间复杂度和希尔排序接近nlog(n)的时间复杂度下的排序性能都是很不错的。

在这场比赛中希尔排序胜出。

【Java】插入排序、希尔排序详解相关推荐

  1. 希尔排序基础java代码_java 算法之希尔排序详解及实现代码

    摘要:这篇Java开发技术栏目下的"java 算法之希尔排序详解及实现代码",介绍的技术点是"希尔排序详解.实现代码.希尔排序.Java.实现.代码",希望对大 ...

  2. Java 插入排序 希尔排序

    目录 一.插入排序 二.希尔排序 一.插入排序 public class test {public static void main(String[] args) {int[] arr = {100, ...

  3. 十大排序详解(java实现)

    十大排序详解(java实现) 一.十大排序算法概述 1.定义 2.分类 3.比较 4.相关概念 二.各算法原理及实现 1.冒泡排序 2.简单选择排序(Selection Sort) 3.直接插入排序( ...

  4. 20191007:选择排序,插入排序,冒泡排序详解

    选择排序,插入排序,冒泡排序详解 描述 图例 代码实现 描述 选择排序:将要排序的对象分作两部份,一个是已排序的,一个是未排序的,从后端未排序部份选择一个最小值,并放入前端已排序部份的最后一个. 插入 ...

  5. c语言排序常用问题,【更新中】【排序详解】解决排序问题(以C语言为例)

    [更新中][排序详解]解决排序问题(以C语言为例) [更新中][排序详解]解决排序问题(以C语言为例) 文章目录 排序的相关概念 简单排序 一.插入排序: (一)插入排序基本思想 (二)插入排序基本操 ...

  6. Sorting 排序详解(c语言实现)

    Sorting 排序详解(c语言实现)# 今日突然有任务,明天补充完整. 邮箱:Is_Dmy@163.com期待交流. Hello,各位小伙伴~我是你们的课代表橙橙,今天呢我要给大家分享的是关于内排序 ...

  7. Java 8 Stream API详解--转

    原文地址:http://blog.csdn.net/chszs/article/details/47038607 Java 8 Stream API详解 一.Stream API介绍 Java 8引入 ...

  8. java定时任务框架elasticjob详解

    这篇文章主要介绍了java定时任务框架elasticjob详解,Elastic-Job是ddframe中dd-job的作业模块中分离出来的分布式弹性作业框架.该项目基于成熟的开源产品Quartz和Zo ...

  9. 蘑菇街2015校招 Java研发笔试题 详解,2015java

    蘑菇街2015校招 Java研发笔试题 详解,2015java 1. 对进程和线程描述正确的是( ) A.  父进程里的所有线程共享相同的地址空间,父进程的所有子进程共享相同的地址空间. B.  改变 ...

最新文章

  1. 【转】Extending Lync Server routing with MSPL
  2. 个人代码库の全局快捷键
  3. Redis基础1(定义及基础)
  4. 记录‘launch_simulation‘failed due to earlier errors的一个解决方法
  5. 程序员都在用的电脑小技巧,一遍就学会,每天早下班一小时
  6. 虚拟机修改默认SSH端口号为10022
  7. 基于云上 Arm 架构赋能数值天气预报
  8. MATLAB混度系统仿真其二:蔡氏电路系统和三阶RC梯形移相振荡器仿真
  9. 公积金贷款额度根据什么而定
  10. No resource found that matches the given name 'Theme.AppCompat.Light.DarkActionBar'
  11. visual studio 批量注释与取消批量注释快捷键
  12. Vue 实现 Hover 功能( mouseover 与 mouseenter 的区别)
  13. 华为云云筑·开发者年度盛典精彩回顾
  14. Keras学习| ImageDataGenerator的参数
  15. c++内存管理3: new handler、=default、=delete函数
  16. 跨平台2D游戏引擎CloudBox
  17. ogre 开发范例大全(2)
  18. 无畏谣言,中科灵芝孢子油将科学进行到底!
  19. DotnetCore学习笔记之IWebHostBuilder(Web主机)
  20. 改革春风出满地= =#

热门文章

  1. .cast( )函数的使用
  2. 单选框(必选)功能的实现
  3. 【算法】时间和空间复杂度
  4. 【考研高数-高等数学-基础】第二章 导数与微分
  5. 绝缘电阻仪测试仪与绝缘耐压测试仪的区别
  6. Self6D: Self-Supervised Monocular 6D Object Pose Estimation论文翻译
  7. 互联网金融的信息安全(一)新环境的安全形势
  8. 微指创始人任春雷携好机友踏入微商领域
  9. Netty应用:快速了解http各版本的特性 HttpServer的小demo
  10. 在树莓派上用python实现人脸识别(face_recognition,PIL,opencv)