一、插入排序描述
插入排序也称为直接插入排序,是最简单的排序算法。
插入排序在处理大数据时并不高效,因为在决定将元素插入哪个位置之前,需要将被插入元素和有序数据集中的其它元素进行比较,这会随着数据集的增大而增加额外的开销;然而插入排序的优点是,当将元素插入一个有序数据集中时,只需要对有序数据集最多进行一次遍历,而不需要完整地运行算法。这个特性使得插入排序在增量排序中非常高效。
二、稳定性
插入排序是稳定的排序方法。
三、复杂度
时间复杂度:O(n^2),n为要排序的元素个数。
空间复杂度:O(1)。
四、基本思想
插入排序的基本思想:假设待排序的记录存放在数组R[1...n]中。初始时,R[1]自成一个有序区,无序区为R[2...n]。从i=2起至i=n为止,依次将R[i]插入当前的有序区R[1...i-1]中,生成n个记录的有序区。
第i-1趟插入排序:通常将一个记录R[i](i=2,...,n-1)插入到当前的有序区,使得插入后仍保证该区间里的记录是按关键字有序的操作,称为第i-1趟插入排序。
排序过程的某一中间时刻,R被划分成两个子区间:R[1...i-1](已排好序的有序区)和R[i...n](当前未排序的部分,可称为无序区)。
插入排序的基本操作是将当前无序区的第1个记录R[i]插入到有序区R[1...i-1]中适当的位置上,使R[1...i]变为新的有序区。因为这种方法每次使有序区增加一个记录,通常称为增量法。
由插入排序的基本思想很容易得到下面简单的方法:
(1)在当前有序区R[1...i-1]中查找R[i]的正确位置k(1<=k<=i-1)。
(2)将R[k...i-1]中的记录后移一个位置,腾出k位置上的空间插入R[i]。
这里使用升序排序,如果R[i]的关键字大于等于R[1...i-1]中所有记录的关键字,则R[i]就是插入位置。
还有一种改进方法,即查找比较操作和记录移动操作交替进行。其具体做法如下:
将待插入记录R[i]的关键字从右向左依次与有序区中记录R[j](j=i-1,i-2,...,1)的关键字进行比较:
(1)如果R[j]的关键字大于R[i]的关键字,则将R[j]后移一个位置;
(2)如果R[j]的关键字小于或等于R[i]的关键字,则查找过程结束,j+1即为R[i]的插入位置。
关键字比R[i]的关键字大的记录均已后移,所以j+1的位置已经腾空,只要将R[i]直接插入此位置即可完成一趟插入排序。
五、实现
// 插入排序(实现一)
void insert_sort(int a[], int n)
{
int i, j, temp;
for (i = 1; i < n; i++) // 需要选中n-1此
{ /* 暂存下标为i的数;下标从1开始,因为开始时下标为0的数前面没有任何数,此时认为它是排好序的 */
temp = a[i];
for (j = i - 1; j >= 0 && temp < a[j]; j--)
{ /* 如果满足条件就往后挪,最坏情况就是temp比a[0]小,它要放置在最前面 */
a[i+1] = a[j];
}
a[j + 1] = temp; /* 找到小标为i的数的放置位置 */
}
}
// 插入排序(实现二)
// 返回值:如果排序成功返回0;否则返回-1.
int insert_sort(
void *data, <span style="white-space:pre"> </span>/* 比较数据 */
int size, /* 数据元素个数 */
int esize, /* 数据元素大小 */
int (*compare)(const void *key1, const void *key2) // 比较方式
)
{
char *a = (char*)data;
void *key;
int i, j;
key = (char*)malloc(esize); /* 在栈上分配一个元素 */
if (NULL == key)
return -1;
/* 反复向有序区插入无序区元素 */
for (i = 1; i < size; i++)
{
memcpy(key, &a[i * esize], esize);
j = i - 1;
/* 查找插入元素的插入位置 */
while (j >= 0&& compare(&a[j * esize], key) > 0)
{
memcpy(&a[(j + 1) * esize], &a[j * esize], esize);
j--;
}
memcpy(&a[(j + 1) * esize], key, size);
}
free(key);
return 0;
}