介绍
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),
因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
先来看一个问题。一个有序的数组,我们往里面添加一个新的数据后,如何继续保持数据有序呢?很简单,我们只要遍历数组,
找到数据应该插入的位置将其插入即可。
这是一个动态排序的过程,即动态地往有序集合中添加数据,我们可以通过这种方法保持集合中的数据一直有序。而对于一组静态数据,
我们也可以借鉴上面讲的插入方法,来进行排序,于是就有了插入排序算法。
那么插入排序具体是如何借助上面的思想来实现排序的呢?
首先,我们将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。
插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。
重复这个过程,直到未排序区间中元素为空,算法结束。
算法描述
一般来说,插入排序都采用in-place(即只需用到O(1)的额外空间的排序)在数组上实现。具体算法描述如下:
从第一个元素开始,该元素可以认为已经被排序;
取出下一个元素,在已经排序的元素序列中从后向前扫描;
如果该元素(已排序)大于新元素,将该元素移到下一位置;
重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
将新元素插入到该位置后;
重复步骤2~5。
与冒泡排序对比:
在冒泡排序中,经过每一轮的排序处理后,数组后端的数是排好序的。
在插入排序中,经过每一轮的排序处理后,数组前端的数是排好序的。
实现代码
package com.lxg.sort;
import java.util.Arrays;
/**
* @author lxg
* @description 插入排序
* @date 2021/9/8
*/
public class InsertionSort {
public static void main(String[] args) {
int[] nums = {1, 2, 7, 9, 5, 8};
insertionSort(nums);
System.out.println(Arrays.toString(nums));
}
/**
* 插入排序
* @param array
*/
public static void insertionSort(int[] array){
if(array==null || array.length <= 1){
return;
}
//数组长度
int length = array.length;
//要插入的数
int insertNum;
for(int i =0;i<length;i++){
insertNum = array[i];
int j=i-1;
//将已经排好序中大于需要插入的数据都往后移动一位
while(j >= 0 && array[j] > insertNum ){
array[j+1]=array[j];
j--;
}
//将需要插入的数插入要插入的位置
array[j+1] = insertNum;
}
}
/**
* 插入排序2
* @param nums
*/
private static void insertionSort2(int[] nums) {
for (int i = 1, j, n = nums.length; i < n; ++i) {
int num = nums[i];
for (j = i - 1; j >=0 && nums[j] > num; --j) {
nums[j + 1] = nums[j];
}
nums[j + 1] = num;
}
}
}
算法分析
空间复杂度 O(1),时间复杂度 O(n²)。
分情况讨论:
给定的数组按照顺序排好序:只需要进行 n-1 次比较,两两交换次数为 0,时间复杂度为 O(n),这是最好的情况。
给定的数组按照逆序排列:需要进行 n*(n-1)/2 次比较,时间复杂度为 O(n²),这是最坏的情况。
给定的数组杂乱无章:在这种情况下,平均时间复杂度是 O(n²)。
因此,时间复杂度是 O(n²),这也是一种稳定的排序算法。