前两篇为选择排序,这篇开始我们来研究插入排序。先从最容易的简单插入排序开始。
简单插人排序的核心思想是:将待排序的一组序列分为已排好序的和未排序的两个部分;
初始状态时,已排序序列仅包含第一个元素,未排序序列中的元素为除去第一个以外N-1个元素;
此后将未排序序列中的元素逐一插人到已排序的序列中。如此往复,经过N-1次插人后,未排序序列中元素个数为0,则排序完成。
具体到每次插入,将未排序元素依次与已排序元素进行比较,直到找到比已排序元素小的第一个位置然后插入。
算法实现(C#):
// 简单插入排序
public static void InsertionSort(int[] arr)
{
InsertionSort(arr, 0, arr.Length - 1);
}
private static void InsertionSort(int[] arr, int left, int right)
{
// 将序列分为待排序和已排序两部分
// 插入排序最大的特点是每一步都会生成一个已经排序好的序列,且他是稳定的排序,即数值相同的两个元素不会发生相对位置的改变
int p, i, tmp;
for (p = left + 1; p < right + 1; p++)
{
// 取出未排序序列中第一个元素
tmp = arr[p];
// 依次与已排序中元素比较并右移,右移的目的是为了腾出位置放未排序的元素
for (i = p; i > 0 && arr[i - 1] > tmp; i--)
arr[i] = arr[i - 1];
// 放进合适的位置
arr[i] = tmp;
}
}
由该算法的代码可以看出,空间复杂度上,简单插入排序仅需要常数个额外空间;时间复杂度上,函数有2个嵌套的循环,每个循环进行O(N)次比较和交换,因此整个简单插入排序的平均时间复杂度为O(N2)。
在最坏的情况下,对应每一个P,要进行P-1次比较和交换,总共花费N*(N-1)/2+N-1次操作,时间复杂度为O(N2);在最好的情况下,也就是对已经排序好的序列进行排序,第二个循环在第一个(arr[i-1] > tmp)时就跳出,因此总共花费O(N)次操作,时间复杂度为O(N)。
此外,简单插入排序是稳定的排序,我们发现,数值相同的两个记录不会发生相对位置上的改变。