插入排序
1. 算法思想
从第二个数开始,每步将一个待排序的对象,按其排序码大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。
2. 算法详解
待排序列:
从第二个数开始进行插入排序:
从第三个数开始进行插入排序:
从第四个数开始进行插入排序:
从第五个数开始进行插入排序:
从第六个数开始进行插入排序:
从第七个数开始进行插入排序:
最后一个数完成插入排序,结束:
插入排序就是将后边的数与前边的数一一进行比较,将小的数排在前边,例如学校里边排高低个子,当发现前边的同学比自己个子高,就和前边的同学交换一下位置。
3. 算法实现
伪代码
template <typename E>
void inssort(E A[], int n)
{
for(int i=1; i<n; i++)
for(int j=i; j>0 && (A[j]<A[j-1]); j--)
swap(A, j, j-1);
}
此处要注意第二个for循环的条件
for(int j=i; j>0 && (A[j]<A[j-1]); j--)
因为插入排序当前位置前边的数据已经排好序,所以如果满足了当前位置大于前一个数据时(A[j]<A[j-1])就可跳出循环。
完整代码
#include <iostream>
using namespace std;
template <typename E>
void swap(E A[], int a, int b) //数据交换
{
int x = A[a];
A[a] = A[b];
A[b] = x;
}
template <typename E>
void inssort(E A[], int n, int &cnt) //插入排序
{
for(int i=1; i<n; i++)
for(int j=i; j>0 && (A[j] < A[j-1]); j--)
{
swap(A, j, j-1);
cnt++;
}
}
template <typename E>
void print(E A[], int n) //打印序列
{
for(int i=0; i<n; i++)
{
cout << A[i] << " ";
}
cout<<endl;
}
int main()
{
cout << "请输入数据规模:" << endl;
int n;
cin >> n;
cout << "请输入待排序列:" << endl;
int *A = new int[n];
for(int i=0; i<n; i++)
{
cin >> A[i];
}
int cnt = 0;
inssort(A, n, cnt);
cout << "插入排序之后的序列:" <<endl;
print(A, n);
cout << "数据交换次数: " <<cnt<<endl;
return 0;
}
运行结果
4. 算法分析
-
排序的时间开销可用算法执行中的数据比较次数与数据移动次数来衡量。
-
插入排序的程序体是由嵌套的两个for循环组成的。
-
外层的for循环要做n-1次;
-
内层for循环次数依赖于第 i 条记录前面的 i-1 条记录的关键码值小于第i条记录的关键码值的记录数。
-
最差的情况是每条记录都必须要移动到数组的顶端,即原来数组中的原始数据是逆序的,此时总的比较的次数为:第一次执行for循环为1,第二次为2,以此类推。数据交换次数为(2+3+···+n)= n(n-1)/2,比较次数为:(2+3+···+n)= n(n-1)/2,总的时间开销为n(n-1) ,时间复杂度为O(n^2)。
-
最佳的情况是原来的序列就已经排好序,这种情况下每个结点刚进入内部for循环就退出,没有记录需要移动,数据交换次数为:0,总的时间开销即外层for循环的执行次数,时间复杂度为O(n)。
-
故平均时间复杂度为O(n^2),空间复杂度为O(1),
-
插入排序不适合对于数据量比较大的排序应用,适用于数据量级小于千的数据排序。
-
另外这里再引入一个排序算法概念,稳定性:待排序的序列中,存在多个具有相同的关键字的元素,经过排序,这些元素的相对次序保持不变。
-
插入排序是稳定排序,不改变相同元素原来的相对顺序。