插入排序可以说是十分简单的排序算法了,但是完整的完成对该算法的测试,并不是一件容易的事情。其中还有很多其他的知识点需要我们学习:
在本次测试中,采用了两种方法,控制板输入和随机数产生的方法。从中也学到了很多知识
无代码言吊,上代码:
1 # include"iostream" 2 # include "vector" 3 # include "ctime" 4 # include "cstdlib" // 如果我们想通过随机数来验证我们的想法。应该预先给vecot分配内存 5 using namespace std; 6 void insert_sort(vector<int> & ); 7 int main() 8 { 9 const int MAX_NUMBER = 5000;//生成的最大的随机数 10 const int MIN_VALUE = 1; 11 const int MAX_VALUE = 40000; 12 vector<int> a; 13 a.reserve(MAX_NUMBER);//预先分配内存你,预防迭代器的失效。否则内存搬移,迭代器容易失效 14 15 // 采用控制台输入的方式 16 //int temp; 17 //while (cin >> temp) 18 //{ 19 // a.push_back(temp); 20 // if (cin.get() == ' ') //注意这种用法,用的很多。 21 // { 22 // break; 23 // } 24 //} 25 26 //采用随机数生成法。 27 srand((unsigned)time(NULL));//根据时钟生成不同的种子,保证每次运行程序产生不同的随机数 28 for (int i = 1; i <= MAX_NUMBER; i++) 29 { 30 int temp; 31 temp = (rand() % (MAX_VALUE - MIN_VALUE + 1)) + MIN_VALUE;//生成[MIN_VALUE,MAX_VALUE]之间的整数随机数 32 a.push_back(temp); 33 } 34 //cout << "The initial order: "; 数据量太大,不适合输出 35 //vector<int>::iterator ia = a.begin(); 36 //for (; ia != a.end(); ia++) 37 //{ 38 // cout << *ia << " "; 39 //} 40 //cout << endl; 41 clock_t start, finish; 42 double runtime; 43 start = clock(); 44 insert_sort(a); 45 finish = clock(); 46 runtime = (double)(finish - start) / CLOCKS_PER_SEC; 47 cout << "The time of Insert Sort algorithm is:" << runtime << "s" << endl; 48 cout << "The new initial order: "; 49 //for (ia = a.begin(); ia != a.end(); ia++) 50 //{ 51 // cout << *ia << " "; 52 //} 53 //cout << endl; 54 system("pause"); 55 return 0; 56 } 57 58 void insert_sort( vector<int> & sort_a) ///注意,采用vector<int> 是可以作为参数进行传递的,那么是否可以作为返回类型使用呢? 59 { 60 int a_size ,temp; 61 a_size = sort_a.size(); 62 for (int i = 1,j; i < a_size; i++)//注意,将j放在这里声明,不要放在下一个循环中声明,否则会造成多次声明?(但是作用域没变,多次声明应该被禁止,所以没有多次声明?) 63 { 64 temp = sort_a[i]; 65 for (j = i; (j > 0) && (sort_a[j - 1] > temp); j--)//这里先后顺序的差别,导致了数组的越界, 66 { 67 sort_a[j] = sort_a[j-1]; 68 } 69 sort_a[j] = temp; 70 71 } 72 }
对于插入排序算法,其实本身而言并没有什么好说的,算法很简单,总结起来就是三步:
1 从第二个数开始遍历(直到最后一个数),将当前数缓存(temp);
2 对于每一个当前值,比较其前面的所有数和该当前值的大小,如果比当前值大,将这个数向后移动一次(注意,并不会出现数据覆盖丢失数据的情况,思考为什么?)
3 将temp中的数插入到2执行结束后,索引所在的序列号
(如果说的不太清楚,看代码58-72)
图中注释掉的代码为控制台输入的情况:
对于控制台输入的方式,注意:17-23行所在代码,这其中的代码是:未知输入整数的个数,以输入回车键作为结束标志符,其实这很符合人的控制台输入习惯。但 如果我们没有if (cin.get() == ' ')这句判断,无论我们怎么按回车键都不会结束输入,这是为什么呢?因为 cin 的输入对于文件系统是EOF结束,但是对于控制台输入,没有这个东西啊!!!其实有,就是快捷键Ctrl+z,!!!也就是,但我们想结束输出时,按Ctrl+z,再按回车键,就结束了我们的输入!!what a fuck!,麻蛋,这不是智障设计吗?谁输入完了,会按Ctrl+z,这种反人类设计真的蠢到了极点。于是,我们只能采用cin.get()读取字符,判断遇到的回车" ",就结束。这一点,我觉得是c++的iostream设计的很失败的地方(其实包括string),给人一种既不想丢弃c语言的操作,又要引入c++的新特性一样,累赘复杂。马丹,希望有一天能改进吧。
值得关注的一点是 vector<int> &作为插入排序的形参类型(实际上也可以作为返回类型),我本身并不知道可以这么做,只是觉得可以这么做(因为不想采用c语言中的数组或者指针这种方式,想纯粹的采用c++的特性编程),结果发现可以,编译器没有报错。这一点凸显了c++的强大之处。
关于随机数的说明:
控制台输入可以验证算法的正确性,但没法体现算法的效率,我们采用随机数生成的办法就是为了验证算法的执行效率。
关于随机数的声明,需要头文件# include "cstdlib" ,当然采用rand()函数就可以了。问题在于,生成指定范围的随机数,第31行的代码告诉了我们应该怎么做。且,rand()有个特点,每次运行程序,生成的随机数是相同的(我们下次运行程序时,随机数不会变,但我们当然希望随机数可以变,于是就需要一个时间种子,27行的srand()就可以使得随机数随着时间变化变化。当然,我们需要知道算法的执行效率,就需要知道执行时间复杂度。于是需要借用系统时间统计函数clock(),这就又需要头文件ctime了
关于插入排序的执行效率,我们可以根据不同的数据规模,来看看其运行时间
数据个数 | 时间/s |
5000 | 0.830 |
10000 | 3.325 |
20000 | 13.207 |
30000 | 29.878 |
(PS)你可以执行上述代码,修改MAX_NUMBER ,可以看看执行的时间。
从执行结果看,随着数据个数的增加,执行时间的增加很明显,很明显,其变化趋势超过了线性规律,即时间时间复杂度超过了O(n),实际上,插入排序算法的时间复杂度为O(n2),可见插入排序其实并不是一种高效的算法