排序算法之插入排序
上一篇介绍了选择排序算法,本篇介绍插入排序算法。
插入排序同样是非常简单又非常基础的排序算法,同样是逻辑简单、容易实现,好多复杂算法也经常离不开它的身影。和上篇一样,本篇也从“基本原理、排序流程、核心代码、算法性能、稳定性、参考代码”等几个方面介绍这一算法。
基本原理:简单来说就是将一个元素插入到已经排好的序列中,插入之后序列依然有序。不妨先假定第一个元素已经有序,然后第二个元素与第一个元素比较,如果比它小,则第一个元素后移一位,第二个元素前移一位,此时这两个元素已排序;然后第三个元素与此时的第二个元素比较,比它小则前移一位,再与第一个元素比较,比它小再前移一位,直到找到合适位置使三个元素有序……以此类推,直到所有元素都找到合适位置,此时序列已经有序。
排序流程:以下以序列:5 3 0 4 1 9 7 2 6 8为例,红色元素表示每趟插入的元素,加粗元素表示每一趟参与比较的元素,未加粗元素表示未参与比较也未移动的元素。
趟数 | 排序前 | 排序后 | 说明 |
1 | 5 3 0 4 1 9 7 2 6 8 | 3 5 0 4 1 9 7 2 6 8 | 第二个元素3比5小,3前移一位,5后移一位 |
2 | 3 5 0 4 1 9 7 2 6 8 | 0 3 5 4 1 9 7 2 6 8 | 第三个元素0比3和5都小,0移动到3和5前面 |
3 | 0 3 5 4 1 9 7 2 6 8 | 0 3 4 5 1 9 7 2 6 8 | 第四个元素4比3大比5小,4移动到3和5中间,0未参与比较 |
4 | 0 3 4 5 1 9 7 2 6 8 | 0 1 3 4 5 9 7 2 6 8 | 第五个元素1比0大比3小,1移动到0和3中间 |
5 | 0 1 3 4 5 9 7 2 6 8 | 0 1 3 4 5 9 7 2 6 8 | 第六个元素9比前五个元素都大,只需要与相邻的5比较即可,不发生移动 |
6 | 0 1 3 4 5 9 7 2 6 8 | 0 1 3 4 5 7 9 2 6 8 | 第七个元素7比5大比9小,7移动到5和9中间,前四个元素不参与比较 |
7 | 0 1 3 4 5 7 9 2 6 8 | 0 1 2 3 4 5 7 9 6 8 | 第八个元素2比1大比3小,2移动到1和3中间,0未参与比较 |
8 | 0 1 2 3 4 5 7 9 6 8 | 0 1 2 3 4 5 6 7 9 8 | 第九个元素6比5大比7小,6移动到5和7中间,前五个元素不参与比较 |
9 | 0 1 2 3 4 5 6 7 9 8 | 0 1 2 3 4 5 6 7 8 9 | 第十个元素8比7大比9小,8移动到7和9中间,前七个元素不参与比较 |
最终排序结果为:0 1 2 3 4 5 6 7 8 9。
核心代码:以Java为例。
public static void sort(int[] a) { int n = a.length; //数组长度 for (int i = 0; i < n; i++) { //第一个for循环指定排序趟数 for (int j = i; j > 0 && a[j]<a[j-1]; j--) { int swap = a[j]; //第二个for循环使元素比较并移动到合适位置 a[j] = a[j-1]; a[j-1] = swap; } } }
算法性能:对于随机排列的长度为N且所有元素都不相等的序列,插入排序在平均情况下需要~N²/4次比较和~N²/4次交换(~表示大约,下同)。最坏的情况下则需要~N²/2次比较和~N²/2次交换;最好的情况下只需要N-1次比较和0次交换。综上,插入排序的时间复杂度介于O(N)和O(N²)之间,取决于初始序列的排列情况;空间复杂度为O(1)。
稳定性:插入排序是元素在已排好的序列中找到合适位置,通过一步步移动插入到合适的位置。如果元素左边的序列已排好,右边的序列还未排列,通过向左遍历已排序序列并插入,此时相等的元素依然可以保持原有的顺序,相对位置并不会改变,所以插入排序是一种稳定的排序算法。
通过分析插入排序的原理及过程,可以发现对于那些已经有序的序列和所有元素都相等的序列,插入排序只会进行N-1次比较,并不会交换任何元素。由此可见,对于那些部分有序的序列或仅有个别元素位置不正确的序列或有大量元素相等的序列,插入排序算法是非常有效的,当然它也适合规模很小的序列。同时,插入排序不会移动比被插入元素更小的元素,因此它所需要的比较次数平均只有选择排序的一半。
参考代码:以Java为例。
import java.util.Random; /* * 插入排序 */ public class InsertionSort { public static void sort(int[] a) { int n = a.length; //数组长度 for (int i = 0; i < n; i++) { //第一个for循环指定排序趟数 for (int j = i; j > 0 && a[j]<a[j-1]; j--) { int swap = a[j]; //第二个for循环使元素比较并移动到合适位置 a[j] = a[j-1]; a[j-1] = swap; } } } public static void main(String[] args) { Random random = new Random(); int[] arg1 = new int[20]; for(int n=0;n<20;n++){ //从[0-100]中生成20个随机数 arg1[n] = random.nextInt(100); } System.out.println("排序前:"); for (int i = 0; i < arg1.length; i++) { System.out.print(arg1[i]+" "); } System.out.println("\n排序后:"); long startTime = System.currentTimeMillis(); //获取开始时间 sort(arg1); long endTime = System.currentTimeMillis(); //获取结束时间 for (int i = 0; i < arg1.length; i++) { System.out.print(arg1[i]+" "); } System.out.println("\n排序时长:"+(endTime-startTime)+"ms"); } }
运行结果:
排序前: 71 9 66 39 73 93 42 87 40 87 67 42 31 6 66 1 26 21 23 76 排序后: 1 6 9 21 23 26 31 39 40 42 42 66 66 67 71 73 76 87 87 93 排序时长:0ms
转载请注明出处 http://www.cnblogs.com/Y-oung/p/7745197.html
工作、学习、交流或有任何疑问,请联系邮箱:yy1340128046@163.com