插入排序(Insertion Sort)一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法 。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动 。
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序。
例如: 使用插入排序算法将数组 { 4,2,8,0,5,7,1,3,6,9 } 进行升序排序。
实现代码如下所示。
#include<iostream>
using namespace std;
template<class T>
void InsertionSort(T *a, int n)
{
int in, out; //in排好顺序的;out未排好顺序的
for (out = 1; out < n;out++) //out = 0已经默认为排好顺序的
{
T temp = a[out]; //将待插入元素复制为哨兵
in = out;
while (in > 0 && a[in - 1] >= temp) //从后往前查找待插入的位置
{
a[in] = a[in - 1]; //往后挪位置
in--;
}
a[in] = temp; //复制待插入元素到插入位置
}
}
int main()
{
int a[] = { 4,2,8,0,5,7,1,3,6,9 };
cout << "排序前:" << endl;
for (int i = 0; i < 10; i++)
{
cout << a[i] << " ";
}
cout << endl;
InsertionSort(a, 10);
cout << "排序后:" << endl;
for (int i = 0; i < 10; i++)
{
cout << a[i] << " ";
}
cout << endl;
double b[] = { 4.1,2.2,8.3,0.4,5.5,7.6,1.7,3.8,6.9,9.0 };
cout << "排序前:" << endl;
for (int i = 0; i < 10; i++)
{
cout << b[i] << " ";
}
cout << endl;
InsertionSort(b, 10);
cout << "排序后:" << endl;
for (int i = 0; i < 10; i++)
{
cout << b[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
排序前:
4 2 8 0 5 7 1 3 6 9
排序后:
0 1 2 3 4 5 6 7 8 9
排序前:
4.1 2.2 8.3 0.4 5.5 7.6 1.7 3.8 6.9 9
排序后:
0.4 1.7 2.2 3.8 4.1 5.5 6.9 7.6 8.3 9
插入排序算法的详细过程如下图所示。
时间复杂度: 对于长度为 n n n的数组,代码执行的时间都花费在最内层while循环里面的操作上(比较和移动)了。对于 n n n个元素,首先外层for循环要循环 n − 1 n-1 n−1 次,然后内层while循环的循环次数是根据 i n in in来决定的, i n = 1 in = 1 in=1时循环 1 次, i n = 2 in = 2 in=2时循环 2 次,…, i n = n − 1 in = n-1 in=n−1时循环 n − 1 n-1 n−1 次,则总的代码执行次数为 1 + 2 + . . . + ( n − 1 ) = n ( n − 1 ) 2 = 1 2 n 2 − 1 2 n 1+2+...+(n-1)=\frac{n(n-1)}{2}=\frac{1}{2}n^{2}-\frac{1}{2}n 1+2+...+(n−1)=2n(n−1)=21n2−21n。根据复杂度计算规则,保留高阶项,并去掉系数,那么插入排序算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
空间复杂度: O ( 1 ) O(1) O(1)。
稳定性: 由于在比较的时候,对于两个相等的元素,不会进行移动,排序完成后,相同元素之间的先后顺序不变,所以插入排序是稳定的排序,如下图所示。
优化插入排序算法: 对while循环的条件进行简化,以加快代码运行速度,并将插入排序函数分开编写。
对于上述例子,优化实现代码如下所示。
#include<iostream>
using namespace std;
template<class T>
void Insertion(const T &temp, T *a, int j)
{
a[0] = temp;
//int j = i - 1;
while (temp < a[j]) //使用a[0]设置条件,将之前版本的(in > 0 && a[in - 1] >= temp)条件进行简化,加快代码运行速度,j=0时a[j]=a[0]=temp,不满足条件,退出循环
{
a[j + 1] = a[j];
j--;
}
a[j + 1] = temp;
}
template<class T>
void InsertionSort(T *a, int n)
{
//a[0]用来保存排序使用,不能保存原始数据
for (int i = 2; i <= n; i++)
{
T temp = a[i];
Insertion(temp, a, i - 1);
//a[0] = temp;
//int j = i - 1;
//while (temp < a[j]) //使用a[0]设置条件,将之前版本的(in > 0 && a[in - 1] >= temp)条件进行简化,加快代码运行速度,j=0时a[j]=a[0]=temp,不满足条件,退出循环
//{
// a[j + 1] = a[j];
// j--;
//}
//a[j + 1] = temp;
}
}
int main()
{
int a[] = { 0,4,2,8,0,5,7,1,3,6,9 }; //在数组前面多加一个0,用于退出循环的条件
cout << "排序前:" << endl;
for (int i = 1; i < 11; i++)
{
cout << a[i] << " ";
}
cout << endl;
InsertionSort(a, 10);
cout << "排序后:" << endl;
for (int i = 1; i < 11; i++)
{
cout << a[i] << " ";
}
cout << endl;
double b[] = { 0,4.1,2.2,8.3,0.4,5.5,7.6,1.7,3.8,6.9,9.0 };
cout << "排序前:" << endl;
for (int i = 1; i < 11; i++)
{
cout << b[i] << " ";
}
cout << endl;
InsertionSort(b, 10);
cout << "排序后:" << endl;
for (int i = 1; i < 11; i++)
{
cout << b[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
排序前:
4 2 8 0 5 7 1 3 6 9
排序后:
0 1 2 3 4 5 6 7 8 9
排序前:
4.1 2.2 8.3 0.4 5.5 7.6 1.7 3.8 6.9 9
排序后:
0.4 1.7 2.2 3.8 4.1 5.5 6.9 7.6 8.3 9