详解直接插入排序算法与相关的Java版代码实现
作者:飞翔的猫咪 发布时间:2022-06-13 09:06:38
直接插入排序
直接插入排序的思路很容易理解,它是这样的:
1.把待排序的数组分成已排序和未排序两部分,初始的时候把第一个元素认为是已排好序的。
2.从第二个元素开始,在已排好序的子数组中寻找到该元素合适的位置并插入该位置。
3.重复上述过程直到最后一个元素 * 入有序子数组中。
4.排序完成。
示例:
思路很简单,但代码并非像冒泡排序那么好写。首先,如何判断合适的位置?大于等于左边、小于等于右边?不行,很多边界条件需要考虑,而且判断次数太多。其次,数组中插入元素,必然需要移动大量元素,如何控制它们的移动?
实际上,这并不是算法本身的问题,而多少和编程语言有点关系了。有时候算法本身已经很成熟了,到了具体的编程语言还是要稍作改动。这里讲的是Java算法,那就拿Java说事儿。
为了解决上述问题,我们对第二步稍作细化,我们不从子数组的起始位置开始比较,而从子数组尾部开始逆序比较,只要比需要插入的数大,就向后移动。直到不大于该数的数,那么,这个空出来的位置就安放需要插入的数字。因此,我们可以写出以下代码:
InsertArray.java
public class InsertArray {
// 数组
private long[] arr;
// 数组中有效数据的大小
private int elems;
// 默认构造函数
public InsertArray() {
arr = new long[50];
}
public InsertArray(int max) {
arr = new long[max];
}
// 插入数据
public void insert(long value) {
arr[elems] = value;
elems++;
}
// 显示数据
public void display() {
for (int i = 0; i < elems; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// 插入排序
public void insertSort() {
long select = 0L;
for(int i = 1; i < elems; i++) {
select = arr[i];
int j = 0;
for(j = i;j > 0 && arr[j - 1] >= select; j--) {
arr[j] = arr[j - 1];
}
arr[j] = select;
}
}
}
测试类:
TestInsertArray.java
public class TestInsertArray {
public static void main(String[] args) {
InsertArray iArr = new InsertArray();
iArr.insert(85);
iArr.insert(7856);
iArr.insert(12);
iArr.insert(8);
iArr.insert(5);
iArr.insert(56);
iArr.display();
iArr.insertSort();
iArr.display();
}
}
打印结果:
算法性能/复杂度
现在讨论下直接插入算法的时间复杂度。无论输入如何,算法总会进行n-1轮排序。但是,由于每个元素的插入点是不确定的,受输入数据影响很大,其复杂度并不是一定的。我们可以分最佳、最坏、平均三种情况讨论。
1.最佳情况:由算法特点可知,当待排数组本身即为正序(数组有序且顺序与需要的顺序相同,于我们的讨论前提,即为升序)时为最佳,理由是这种情况下,每个元素只需要比较一次且无需移动。算法的时间复杂度为O(n);
2.最坏情况:很显然,当待排数组为逆序时为最坏情况,这种情况下我们的每轮比较次数为i-1, 赋值次数为i。总的次数为级数2n-1的前n项和,即n^2.算法的时间复杂度为O(n^2);
3.平均情况:由上述分析可以得到平均情况下算法的运算次数大约为(n^2)/2(注:这里计算以赋值和比较计,若按移动和比较,则大约为n^2/4),显然,时间复杂度还是O(n^2)。
至于算法的空间复杂度,所有移动均在数据内部进行,唯一的开销是我们引入了一个临时变量(有的数据结构书上称为“哨兵”),因此,其空间复杂度(额外空间)为O(1)。
算法稳定性
由于只需要找到不大于当前数的位置而并不需要交换,因此,直接插入排序是稳定的排序方法。
算法变种
如果待排列的数据比较多,那么每次从后往前找就造成了很大的开销,为了提高查找速度,可以采用二分查找(Binary Search)进行性能优化。由于二分查找的效率很高,保证了O(㏒n)复杂度,在数据比较多或输入数据趋向最坏情况时可以大幅提高查找效率。在有些书上将这种方法称为折半插入排序。它的代码实现比较复杂,以后有时间可以贴出来。
此外,还有2-路插入排序和表插入排序。2-路插入排序是在折半插入排序的基础上进一步改进,其移动次数大为降低,大约为n^2/8。但是,它并不能避免移动次数,也不能降低复杂度级别。表插入排序则完全改变存储结构,不移动记录,但需要维护一个链表,以链表的指针修改代替移动记录。因此,其复杂度仍然是O(n^2)。
有关2-路插入排序和表插入排序,可以参考严蔚敏、吴伟民编著的《数据结构》一书。
算法适用场景
插入排序由于O(n^2)的复杂度,在数组较大的时候不适用。但是,在数据比较少的时候,是一个不错的选择,一般做为快速排序的扩充。例如,在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序。又如,在JDK 7 java.util.Arrays所用的sort方法的实现中,当待排数组长度小于47时,会使用插入排序。


猜你喜欢
- 摘要:想必大家做开发的时候都会用到下拉刷新的控件,现在各种第三方的下拉刷新控件不胜枚举。当然最NB的还是XListView。其他也有针对Gr
- 最近由于工作原因,没时间更新,开始吧~~关于json的返回需要用到一个工具包来将书转换为json格式,在此用到的jar包为: im
- 前言上篇文章通过一个有header和footer的滚动控件(Viewgroup)学了下MeasureSpec、onMeasure以及onLa
- 关于面向对象和封装的个人理解类和对象类:对事物的一种描述(具有共同属性和行为的事物的抽象),例如手机,属性:品牌价格,行为:玩游戏,刷vx;
- 一、项目需求二、项目思路1、菜单制作2、中奖号码生成 getNumber (随机数 Math.random)3、购买号码和中奖号码比对 生成
- 一家移动互联网公司,说到底,要盈利总是需要付费用户的,自己开发支付系统对于资源有限的公司来说显然不太明智,国内已经有多家成熟的移动支付提供商
- 本文介绍一些Java初学者常问的问题,可以用%除以一个小数吗? a += b 和 a = a + b 的效果有区别吗? 声明一个数组为什么需
- 【1】阻塞队列一、什么是阻塞队列?① 支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。② 支持阻塞的移除方法:
- 前言很多时候,当你以为掌握了事实真相的时间,如果你能再深入一点,你可能会发现另外一些真相。比如面向切面编程的最佳编程实践是AOP,AOP的主
- 1.异步委托开启线程public class Program { public static void
- Spring Boot简介SpringBoot为了简化在开发基于 Spring的项目的难度,减少了哪些繁杂的配置,从而让开发基于 Sprin
- 目录1 Exchanger 介绍2 Exchanger 实例exchange等待超时3 实现原理1 Exchanger 介绍前面分别介绍了C
- 我想,对于各位使用面向对象编程语言的程序员来说,“接口”这个名词一定不陌生,但是不知各位有没有这样的疑惑:接口有什么用途?它和抽象类有什么区
- maven打包时候修改包名称带上git版本号和打包时间使用 maven 插件 git-commit-id-plugin 可以获取项目的git
- 在前面讲到了《基于任务的异步编程模式(TAP)》,但是如果调用异步方法,没有等待,那么调用异步方法的线程中使用传统的try/catch块是不
- MoshiMoshi是一个对Kotlin更友好的Json库,square/moshi: A modern JSON library for
- 本篇博客给大家分享一个效果比较好的侧滑菜单的Demo,实现点击左边菜单切换Fragment。效果如下: 主Activity代码:p
- 前言在日常开发中,除了修改请求参数、设置响应header,响应body外,还有一种需求就是url重新,或者是修改url,这里简述一下怎么在z
- 本文实例为大家分享了java实现航空用户管理系统的具体代码,供大家参考,具体内容如下题目内容:某航空公司在其航班到达的不同的国家的不同地方设
- 定义在一幅无向图G=(V,E) 中,(u,v) 为连接顶点u和顶点v的边,w(u,v)为边的权重,若存在边的子集T&am