绝知此事要躬行之——插入排序

插入排序可以说是十分简单的排序算法了,但是完整的完成对该算法的测试,并不是一件容易的事情。其中还有很多其他的知识点需要我们学习:

在本次测试中,采用了两种方法,控制板输入和随机数产生的方法。从中也学到了很多知识

无代码言吊,上代码:

 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() == '\n') //注意这种用法,用的很多。
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() == '\n')这句判断,无论我们怎么按回车键都不会结束输入,这是为什么呢?因为 cin 的输入对于文件系统是EOF结束,但是对于控制台输入,没有这个东西啊!!!其实有,就是快捷键Ctrl+z,!!!也就是,但我们想结束输出时,按Ctrl+z,再按回车键,就结束了我们的输入!!what a fuck!,麻蛋,这不是智障设计吗?谁输入完了,会按Ctrl+z,这种反人类设计真的蠢到了极点。于是,我们只能采用cin.get()读取字符,判断遇到的回车"\n",就结束。这一点,我觉得是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),可见插入排序其实并不是一种高效的算法

posted @ 2019-05-02 17:04  少年π  阅读(221)  评论(0编辑  收藏  举报