注:本文为《算法导论》中排序相关内容的笔记。对此感兴趣的读者还望支持原作者。
基本概念
所谓插入排序,即将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,适用于少量数据的稳定排序,时间复杂度为 O ( n 2 ) O(n^2) O(n2)。既然插入排序是用于数据的排序,不如就顺便阐述一下排序的概念:
输入:n个数的一个序列 ( a 1 , a 2 , … , a n ) (a_1, a_2,\ldots, a_n) (a1,a2,…,an).
输出: 输入序列的一个排列 ( a 1 ′ , a 2 ′ , … , a n ′ ) (a^{'}_1,a^{'}_2,\ldots,a^{'}_n) (a1′,a2′,…,an′),满足 a 1 ′ ≤ a 2 ′ ≤ a n ′ a^{'}_1\leq a^{'}_2\leq a^{'}_n a1′≤a2′≤an′。
其中,输出为非降序序列。当然,在实际生活中,也可以是非升序排序,视情况而定。
算法流程
其实,插入排序的工作方式与我们日常生活中排序一手扑克牌极其相似。开始时,我们左手为空且桌子上的牌面向下。然后,我们每次从桌子上拿走一张牌并将它插入左手中正确的位置,我们从右到左将它与已在手中的每张牌进行比较。如此,左手中的牌总是排序好的。
代码示例
千言万语,不如代码一段,废话少说,直接上代码。此代码段给出了简单的插入排序的Java版本。
/**
* 插入排序的简单实现,非降序(整型数组)
* @author 爱学习的程序员
* @version V1.0
* */
import java.util.Random;
public class InsertSort{
// 排序
public static void sort(int[] arr){
int n = arr.length;
// 数组错误
if(n <= 0)
return;
// 数组无需排序
else if(n ==1 )
return;
else{
int i ,j = 0;
// 中间变量,用于交换
int temp = 0;
for(i = 1; i < n; i++){
// 获取待排序变量
temp = arr[i];
// 开始排序(由插入排序算法流程可知,此时数组a[0...i-1]已经有序)
j = i - 1;
// 逐个比较,确定待排序元素的插入位置
while(j >= 0 && temp < arr[j]){
arr[j+1] = arr[j];
j--;
}
arr[j+1] = temp;
}
}
}
public static void main(String[] args){
//测试数组生成
int[] arr = new int[10];
Random rand = new Random();
System.out.println("测试数组:");
int i = 0;
int length = arr.length;
for(i = 0; i < length; i++){
arr[i] = rand.nextInt(100);
System.out.print(arr[i]+" ");
}
// 排序
sort(arr);
// 输出排序结果
System.out.println("\n"+"排序结果");
for(i = 0; i < length; i++)
System.out.print(arr[i]+" ");
}
}
算法分析
其实,从示例代码中不难看出,插入排序的时间复杂度主要由确定待排序元素的插入位置以及元素的移动。因此,在最好情况下,输入数组已排好序,则此时无需寻找待排序元素的插入位置以及移动元素。因此,在最好情况下,插入排序的时间复杂度为O(n)。当然,“每个硬币都有两面”,插入排序有最好情况下,则也有最坏情况,即输入数组方向排序。在最坏情况下,待排序元素需要和之前有序的子数组的每个元素进行比较,之后子数组内的每个元素都需要后移,则此时插入排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。而在算法研究的大多数情况下,我们往往更加关注算法在最坏情况下的时间复杂度。
算法总结
-
优点
从上述中不难看出,插入排序思想简单,易实现,且排序结果稳定。
-
缺点
插入排序时间复杂度为 O ( n 2 ) O(n^2) O(n2),低效,无法用于大规模数据的排序。