本来算法分析系列算是写完了(其中我忽略了数学归纳法那一部分内容,因为我觉得我们高中学过就没必要再提一次)。但是教材强调我们要重点看看练习题的第二题。我也看了一下,确实很不错。这道题讨论的是插入排序(insertion sort)算法。题目如下:
练习2——插入排序(insertion sort)
还有其他几种排序算法表现出选择排序的O(N^2)行为。 其中最重要的是插入排序,其操作如下。你可以依次选择vector0中的每个元素,与选择排序算法一样。但是,在该过程的每个步骤中,目标不是找到最小的剩余值并将其切换到正确的位置,而是确保到目前为止所考虑的值是相对于彼此正确排序的。虽然这些值可能会随着更多元素的处理而改变,但它们在自身中形成有序的序列。
例如,如果你再次考虑我们之前排序示例中使用的数据,则插入排序算法的第一个循环不需要工作,因为一个元素的向量总是被排序:
在下一个循环中,你需要将25放在正确的位置,这意味着您需要交换56和25以达到以下配置:
在第三个循环,你需要找到值37应该去哪里。为了做到这一点,你需要向前遍历早期的元素,你知道这些元素是已经排好序的。当你走的时候,你需要把每一个较大的元素一个位置向右移动,这最终为你要插入的价值腾出空间。 在这种情况下,56被向后移动了一个位置,37个在第1位上升。因此,第三个循环后的配置如下所示:
在每个循环之后,vector的初始部分总是被排序,这意味着以这种方式循环遍历所有位置将对整个vector进行排序。
插入排序算法在实践中很重要,因为如果向量已经以正确的顺序或者已经或多或少地排好序,则它在线性时间运行(因为此时只是相当于遍历)。因此,我们通常使用插入排序将只有少数元素并未被排序的的大型vector中。这样耗时是最少的。
目的:编写一个程序实现插入排序算法(用sort作为函数原型)。 构造一个非正式的参数来表明插入排序的最坏情况是O(N^2)
代码的实现
我刚刚花了20分钟写出了这个简短的代码,实话,惭愧写了那么久。但是我确实学到了新的技巧,现在来分享:
#include <iostream>
#include <vector>
using namespace std;
/*函数原型*/
void sort(vector<int> & vec);
/*主函数*/
int main(){
vector<int> vec;
for(int i = 0; i < 8; i++){
int n;
cin >> n;
vec.push_back(n);
}
sort(vec);
for(int k = 0; k < vec.size(); k++){
cout << vec[k] << " ";
}
return 0;
}
void sort(vector<int> &vec){
for(int i = 1; i < vec.size(); i++){
int key = vec[i];//记录该位置的值,这个位置之前的数字都已经被排序好
//满足条件之后,往后挪动为插入腾出空间
while(key < vec[i - 1] && i > 0){
vec[i] = vec[i- 1];
i--;
vec[i] = key;
}
}
}
运行结果如图:
代码分析与反思
其实写这段代码的时候头脑还是挺清晰的。在vec[i]前面的数一定是排序好的,只要vec[i]前面的数比它小,那么就插入到这个数的前面,然后继续比较前者是不是还比它小。我们重点解析一下这段代码:
while(key < vec[i - 1] && i > 0){
vec[i] = vec[i- 1];
i--;
vec[i] = key;
}
我们假设这个时候i = 2;那么数据中的37前面的25和56是已经排序好的。我们先将vec[i - 1]的值先赋值给vec[i].这个时候
数据就是:
25 | 56 | 56 | ….. |
---|---|---|---|
0 | 1 | 2 | ……… |
这就相当于我们完成了后移,为插入腾出空间。此时 i 减去1,再用记录了37的key赋值给vec[i] (此时就是vec[1]),完成插入)。那我们能不能把
vec[i] = key;
放在while循环的后面呢?当然是可以的,前者是边后移边插入,后者是先全部后移,然后插入。
现在我们可以来回答提出的两个问题了,最好的情况是什么呢?当然是已经排好序的时候啦。因为我们的操作是遍历,然后比较大小再进行插入操作的。如果所有的数据都是后一位大于前一位,那么说明的就是while循环根本都不用执行,因此我们所做的也就仅仅是遍历的过程,也就是线性的复杂度O(N),好了 现在我们再讨论一下最坏的情况,是排序一组毫无规则的vector吗?很明显不是的,因为再没有规则它前面都可以有比它小或者大的数,意味着它不用进行所有的插入操作了。最坏的情况我想应该就是让你去用插入排序去将一组降序排列的vector变为升序排序的vector了吧,就像 :
8 7 6 5 4 3 2 1
用插入排序排成:
1 2 3 4 5 6 7 8
每个操作都要移动N-1次。那么我们进行了N次,所以显然跟选择排序的s算法复杂度一样为O(N^2)
C++抽象编程——算法分析(1)——选择排序