C++算法之插入排序
一、算法描述
整理插入排序算法描述如下:
- 枚举序列中第
2~n
个元素。 - 当枚举元素i时,前
i-1
个元素已经有序。将第i个元素插入到前i-1
个元素的有序序列中,形成长度为i的有序序列。 - 枚举过程结束后,整个序列有序。
在上述算法描述中,我们有个关键的步骤——插入操作:将第i个元素插入到前i-1
个元素的有序序列中,形成长度为i的有序序列。
怎样实现这个过程呢?
一种实现思路和前面介绍的“火车站插队”过程十分类似。比如对于如下序列:
这个序列的前4
个元素已经有序,现在我们要把2
插入到前4
个元素中去。
1、首先让2
出队。
2、然后将其与6
比较,发现2<6
,说明2
应该在6
前面。所以我们将6
向后移动。
3、再将其与5
比较,发现2<5
,说明2
应该排在5
前面。所以我们将5
向后移动。
4、经过同样的分析,4
也应该向后移。
5、最后我们将2
与1
比较,发现1<2
,说明2
应该放在1
的后面。正好经过前面的移动,1后面有一个空位,所以我们把2
插入在这个空位中。
以上就是我们将一个新的元素2
插入前面有序序列a的过程。
因为前面的序列有序,所以在整个序列中一定有一条分界线j
,使得分界线前面的元素(i<j)
,都满足a[i]<=2
;
对于分界线及分界线以后的元素(i>=j)
,都满足a[i]>2
;
所以,上面的移动一定可以将分界线及以后的元素向后移动一位,空出分界线的位置j,然后将2
插入到j
号元素中。
所以,我们总结一下插入操作的算法描述:
-
假设序列
1~(i-1)
已经有序, 从i
到1
枚举分界线的下标j; -
如果分界线前面的元素
a[j-1]
大于x
,说明a[j-1]
应该在分界线后面。所以将a[j-1]
移动到a[j]
,分界线前移变成j-1
。 -
如果分界线前面没有元素
(j=1)
,就将x
放在数组第1
位。否则如果碰到一个j-1
号元素小于等于x
,说明分界线位置正确,就将x
插到j
位。
二、代码实现
代码如下(示例):
#include <bits/stdc++.h>
#define N 1550
using namespace std;
int a[N], n;
int main() {
// 输入
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
// 插入排序
for (int i = 2; i <= n; ++i) { // 按照第2个到第n个的顺序依次插入
int j, x = a[i]; // 先将i号元素用临时变量保存防止被修改。
// 插入过程,目的是空出分界线位置j,使得所有<j的部分<=x,所有>j的部分>x。
// 循环维持条件,j>1,并且j前面的元素>x。
for (j = i; j > 1 && a[j - 1] > x; --j) {
// 满足循环条件,相当于分界线应向前移,
// 分界线向前移,就等于将分界线前面>x的元素向后移
a[j] = a[j - 1];
}
// 找到分界线位置,插入待插入元素x
a[j] = x;
}
// 输出
for (int i = 1; i <= n; ++i) cout << a[i] << ' ';
cout << endl;
return 0;
}
三、复杂度分析
插入排序的总时间复杂度是O(n^2)