【编程珠玑】读书笔记 第十一章 排序

2013-07-15 20:18:41

本章以排序为例,展示了编程过程中的一般步骤。

主要针对插入排序、快速排序进行了讨论。

之前看过一本书《剑指offer》,感觉写的很好,有具体的实例,对每个题都给出思路以及实例,并对编程中的要点进行说明,代码风格也很好,现在看这本《编程珠玑》,是因为想多了解一些编程以及算法方面的理论。

书快看完了,个人感觉这本书内容还不错,但是表达方式比较晦涩,不太好理解,比如说代码的思路说明不够清楚、编程的风格也比较糟糕,有些术语也不太好理解,比如脚手架(书中的意思是测试框架)、程序验证(书中的意思是证明编程思路的正确性)、算法调优、代码调优等。

本章的内容有些表叔还是很晦涩,排序本来是很简单的,而本章中提到的思路有的却是不好理解(有的效率还比较低),看了很长时间,才看懂。

下面给出根据书中的思路写出的完整代码,以及测试“脚手架”,“脚手架”在此处的测试却是挺好用。后面附上了测试结果,给出了待排序数组为1000,000时每种排序的时间消耗。

从测试结果可以看到,同样的排序,最快的是采用函数QuickSortAdvaced(使用随机枢轴)与QuickSortImproved_2,标准库函数qsort与QuickSortImproved_1差不多,QuickSortBasic也比较快,且比QuickSortImproved_1差不多快一倍;

而插入排序很慢,最快的插入排序大概是最快的快速排序QuickSortAdvaced的200倍;

最慢的插入排序是最快的插入排序的100倍,可见即使是同样的算法,代码的编写对程序的效率也是很重要的,要从代码上进行优化.

 

 

QuickSortBasic在划分时,使用的单向划分,在数组元素都相同的极端情况下,需要n-1次划分,而不是log2(n)次,因此时间复杂度恶化为O(n^2);

为了改善这种情况,提出了双向划分,即为下面代码中的QuickSortImproved_1,该算法在数组元素都相同的极端情况下,可将规模减半,因此时间复杂度为O(nlog2(n)),为快速排序的最好性能;

QuickSortImproved_1的双向换分是书中给出的代码,可能是先入为主的原因,之前看到有QuickSortImproved_2中代码的写法,个人感觉QuickSortImproved_1中的写法不是很好理解,且交换次数增加很多,因此有了QuickSortImproved_2,且从运行时间上看,QuickSortImproved_2比QuickSortImproved_1要快1倍还多,若增加输入规模,速度的差别会显著;

对于QuickSortImproved_2,要注意到,Partion函数主循环while (i < j)内的循环的条件是 while (unsortedArray[j] > unsortedArray[i] && i < j),而非 while (unsortedArray[j] >= unsortedArray[i] && i < j),另外一个循环是while (unsortedArray[j] < unsortedArray[i] && i < j),而非 while (unsortedArray[j] <= unsortedArray[i] && i < j),也就是两个循环都是不包含=的,这样才能保证Partion在数组元素都相同的极端情况下,将规模减半;否则,就会使最坏的情况。

假设输入数组为已经排好序的数组,那么使用上述的算法(QuickSortBasic、QuickSortImproved_1、QuickSortImproved_2),时间性能也是最差的O(n^2),至此,我们已经做了两种极端情况的分析,一种是数组元素都相同,我们通过双向划分解决,但该方法不能解决数组已经排好序的情况,还有没有其他的极端情况?快速排序的复杂度到底决定于哪些因素?怎样才能达到最佳性能?

在《算法导论》的7.2节指出,快速排序的运行时间与划分是否对称有关,而后者又与选择了哪一个元素来进行划分有关。如果划分是对称的,那么本算法从渐进意义上来讲,就与合并算法一样快;如果划分是不对称的,那么本算法渐进上就和插入算法一样慢。

那么,要想得到对称的划分,使用了随机划分的方法,可以得到近似对称的划分,如QuickSortAdvaced所示。QuickSortAdvaced用函数PartionRandomPivot随机选择枢轴元素,从而得到近似对称的划分。

 

测试环境:win7 32bit + VS2010

代码:

  1 #include <iostream>
  2 #include <cassert>
  3 #include <time.h>
  4 using namespace std;
  5 
  6 typedef int DataType;
  7 const int MaxSize = 100000000;
  8 const int SortCutOff = 50;
  9 
 10 //输入合法性检查
 11 void CheckInvalid(int array[],int len)
 12 {
 13     assert(NULL != array && len > 0);
 14 }
 15 
 16 //直接插入排序基本版本
 17 void InsertSortBasic(int unsortedArray[],int len)
 18 {
 19     int i;
 20     int j;
 21 
 22     CheckInvalid(unsortedArray,len);
 23 
 24     for (i = 1;i < len;++i)
 25     {
 26         for (j = i;j >= 0 && unsortedArray[j] < unsortedArray[j - 1];--j)
 27         {
 28             swap(unsortedArray[j],unsortedArray[j - 1]);
 29         }
 30     }
 31 }
 32 
 33 //直接插入排序改进,将for循环中的swap函数改为内联的,提高效率
 34 void InsertSortImproved(int unsortedArray[],int len)
 35 {
 36     int i;
 37     int j;
 38     int tmp;
 39 
 40     CheckInvalid(unsortedArray,len);
 41 
 42     for (i = 1;i < len;++i)
 43     {
 44         for (j = i;j >= 0 && unsortedArray[j] < unsortedArray[j - 1];--j)
 45         {
 46             tmp = unsortedArray[j];
 47             unsortedArray[j] = unsortedArray[j - 1];
 48             unsortedArray[j - 1] = tmp;
 49         }
 50     }
 51 }
 52 
 53 
 54 //直接插入排序的进一步改进
 55 void InsertSortAdvacaed(int unsortedArray[],int len)
 56 {
 57     int i;
 58     int j;
 59     int t;
 60 
 61     CheckInvalid(unsortedArray,len);
 62 
 63     for (i = 1;i < len;++i)
 64     {
 65         t = unsortedArray[i];
 66         for (j = i - 1;j >= 0 && unsortedArray[j] > t;--j)
 67         {
 68             unsortedArray[j + 1] = unsortedArray[j];
 69         }
 70         unsortedArray[j + 1] = t;
 71     }
 72 }
 73 
 74 //快速排序的基本版本,从一个方向进行划分
 75 void QuickSortBasic(int unsortedArray[],int begin,int end)
 76 {
 77     if (begin >= end)
 78     {
 79         return;
 80     }
 81 
 82     int i;
 83     int mid = begin;
 84     int tmp;
 85 
 86     for (i = begin + 1;i <= end;++i) //注意终止条件不是i < end
 87     {
 88         if (unsortedArray[i] < unsortedArray[begin])
 89         {
 90             ++mid;
 91             tmp = unsortedArray[i];
 92             unsortedArray[i] = unsortedArray[mid];
 93             unsortedArray[mid] = tmp;
 94         }
 95     }
 96 
 97     swap(unsortedArray[begin],unsortedArray[mid]);
 98     QuickSortBasic(unsortedArray,begin,mid - 1);
 99     QuickSortBasic(unsortedArray,mid + 1,end);
100 }
101 
102 //快速排序的改进,从两个方向划分
103 void QuickSortImproved_1(int unsortedArray[],int begin,int end)
104 {
105     if (begin >= end)
106     {
107         return;
108     }
109 
110     int i, j;
111     DataType t;
112     t = unsortedArray[begin];
113     i = begin;
114     j = end + 1;
115 
116     for (;;) 
117     {
118         do i++; 
119         while (i <= end && unsortedArray[i] < t);
120         do j--; 
121         while (unsortedArray[j] > t);
122         if (i > j)
123             break;
124         swap(unsortedArray[i],unsortedArray[j]);
125     }
126     swap(unsortedArray[begin],unsortedArray[j]);
127     QuickSortImproved_1(unsortedArray,begin,j - 1);
128     QuickSortImproved_1(unsortedArray,j + 1,end);
129 }
130 
131 //将a_unsorted的首个元素放到排序后的位置,返回该数据的位置
132 int Partion(int unsortedArray[],int begin,int end)
133 {
134     int i = begin;
135     int j = end;
136     int tmp;
137 
138     while (i < j)
139     {
140         while (unsortedArray[j] > unsortedArray[i] && i < j)  //从后向前比较,知道发现元素不大于轴a_unsorted[i]
141         {
142             --j;    
143         }
144 
145         if (i < j)
146         {
147             tmp =  unsortedArray[i];    //交换之后,轴变为a_unsorted[j]
148             unsortedArray[i] = unsortedArray[j];
149             unsortedArray[j] = tmp;
150             ++i;        //缩小范围
151         }
152 
153         while (unsortedArray[i] < unsortedArray[j] && i < j)//从前向后比较,知道发现元素不小于轴a_unsorted[j]
154         {
155             ++i;    
156         }
157 
158         if (i < j)
159         {
160             tmp = unsortedArray[i];        //交换之后,轴变为a_unsorted[i]
161             unsortedArray[i] = unsortedArray[j];
162             unsortedArray[j] = tmp;
163 
164             --j;     //缩小范围
165         }
166     }
167 
168     return i;
169 }
170 
171 //快速排序,更好的写法
172 void QuickSortImproved_2(int unsortedArray[],int begin,int end)
173 {
174     if (begin >= end)
175     {
176         return;
177     }
178 
179     int mid = Partion(unsortedArray,begin,end);
180 
181     QuickSortImproved_2(unsortedArray,begin,mid - 1);
182     QuickSortImproved_2(unsortedArray,mid + 1,end);
183 }
184 
185 //产生在[lowBound,upperBound - 1]区间的随机数
186 int RandomIntGenerate(int lowBound, int upperBound)
187 {    
188     return (lowBound + (RAND_MAX * rand() + rand()) % (upperBound - lowBound + 1) );
189 }
190 
191 //将a_unsorted的首个元素放到排序后的位置,返回该数据的位置
192 int PartionRandomPivot(int unsortedArray[],int begin,int end)
193 {
194     int i = begin;
195     int j = end;
196     int tmp;
197 
198     int pivotIndex =  RandomIntGenerate(begin,end);
199     swap(unsortedArray[begin],unsortedArray[pivotIndex]);
200 
201     while (i < j)
202     {
203         while (unsortedArray[j] > unsortedArray[i] && i < j)  //从后向前比较,知道发现元素不大于轴a_unsorted[i]
204         {
205             --j;    
206         }
207 
208         if (i < j)
209         {
210             tmp =  unsortedArray[i];    //交换之后,轴变为a_unsorted[j]
211             unsortedArray[i] = unsortedArray[j];
212             unsortedArray[j] = tmp;
213             ++i;        //缩小范围
214         }
215 
216         while (unsortedArray[i] < unsortedArray[j] && i < j)//从前向后比较,知道发现元素不小于轴a_unsorted[j]
217         {
218             ++i;    
219         }
220 
221         if (i < j)
222         {
223             tmp = unsortedArray[i];        //交换之后,轴变为a_unsorted[i]
224             unsortedArray[i] = unsortedArray[j];
225             unsortedArray[j] = tmp;
226 
227             --j;     //缩小范围
228         }
229     }
230 
231     return i;
232 }
233 
234 //快速排序的进一步改进,
235 //随机选择枢轴元素
236 //且在数组长度小于一定值(此处为SortCutOff)时,用插入排序
237 void QuickSortAdvaced(int unsortedArray[],int begin,int end)
238 {
239     if (begin >= end)
240     {
241         return;
242     }
243 
244     if((end - begin + 1) < SortCutOff)
245     {
246         InsertSortAdvacaed(unsortedArray,(end - begin + 1));
247         return;
248     }
249 
250     int mid = PartionRandomPivot(unsortedArray,begin,end);
251 
252     QuickSortImproved_2(unsortedArray,begin,mid - 1);
253     QuickSortImproved_2(unsortedArray,mid + 1,end);
254 }
255 
256 int IntCompare(const void *_p,const void *_q)
257 {    
258     int *p = (int *) _p;
259     int *q= (int *) _q;
260     return (*p - *q);
261 }
262 
263 void DisplayArray(int array[],int len)
264 {
265     CheckInvalid(array,len);
266 
267     for (int i = 0;i < len;++i)
268     {
269         cout<<array[i]<<"\t";
270     }
271     cout<<endl;
272 }
273 
274 //测试“脚手架”
275 void TestDriver()
276 {    
277     //int unsortedArray[MaxSize];
278     int *unsortedArray = new int[MaxSize];
279     size_t programToTest;
280     size_t lengthOfUnsortedArray;
281     int MinRandomInt;
282     int MaxRandomInt;
283     size_t i;
284     int timeStart = 0;
285     double timeCostAverage = 0;
286     
287     cout<<"the identifier of the program is :"<<endl;
288     cout<<"InsertSortBasic : 11"<<endl;
289     cout<<"InsertSortImproved : 12"<<endl;
290     cout<<"InsertSortAdvacaed : 13"<<endl;
291     cout<<"QuickSortBasic : 21"<<endl;
292     cout<<"QuickSortImproved_1 : 22"<<endl;
293     cout<<"QuickSortImproved_2 : 23"<<endl;
294     cout<<"QuickSortAdvaced : 24"<<endl;
295     cout<<endl;
296 
297     cout<<"please enter the length Of UnsortedArray,MinRandomInt and MaxRandomInt :"<<endl;
298     cin>>lengthOfUnsortedArray>>MinRandomInt>>MaxRandomInt;
299     cout<<"please enter the identifier of the program to test (end with ctrl+z): "<<endl;
300     while (cin>>programToTest)
301     {
302         for (i = 0;i < lengthOfUnsortedArray;++i)  //准备待排序数组
303         {
304             unsortedArray[i] = RandomIntGenerate(MinRandomInt,MaxRandomInt);
305         }
306 
307     /*    cout<<"the unsorted array is :"<<endl;
308         DisplayArray(unsortedArray,lengthOfUnsortedArray);*/
309         timeStart = clock();
310         switch (programToTest)
311         {
312             case 11: 
313                 cout<<"Test InsertSortBasic..."<<endl;
314                 InsertSortBasic(unsortedArray,lengthOfUnsortedArray);
315                 break;
316 
317             case 12: 
318                 cout<<"Test InsertSortImproved..."<<endl;
319                 InsertSortImproved(unsortedArray,lengthOfUnsortedArray);
320                 break;
321 
322             case 13: 
323                 cout<<"Test InsertSortAdvacaed..."<<endl;
324                 InsertSortAdvacaed(unsortedArray,lengthOfUnsortedArray);
325                 break;
326             case 21: 
327                 cout<<"Test QuickSortBasic..."<<endl;
328                 QuickSortBasic(unsortedArray,0,lengthOfUnsortedArray - 1);
329                 break;
330             case 22: 
331                 cout<<"Test QuickSortImproved_1..."<<endl;
332                 QuickSortImproved_1(unsortedArray,0,lengthOfUnsortedArray - 1);
333                 break;
334             case 23: 
335                 cout<<"Test QuickSortImproved_2..."<<endl;
336                 QuickSortImproved_2(unsortedArray,0,lengthOfUnsortedArray - 1);
337                 break;
338             case 24: 
339                 cout<<"Test QuickSortAdvaced..."<<endl;
340                 QuickSortAdvaced(unsortedArray,0,lengthOfUnsortedArray - 1);
341                 break;
342             case 25: 
343                 cout<<"Test qsort..."<<endl;
344                 qsort(unsortedArray,lengthOfUnsortedArray,sizeof(DataType),IntCompare);
345                 break;
346             default:
347                 break;
348         }
349 
350         timeCostAverage = 1e9 * ( clock() - timeStart ) / ( CLOCKS_PER_SEC * lengthOfUnsortedArray );
351         cout<<"the average time cost per data is : "<<timeCostAverage<<" ns"<<endl;
352 
353         for (i = 0;i < lengthOfUnsortedArray - 1;++i)
354         {
355             if (unsortedArray[i] > unsortedArray[i + 1])
356             {
357                 cout<<"sort bug i = "<<i<<endl;
358             }
359         }
360 
361         /*cout<<"the sorted array is :"<<endl;
362         DisplayArray(unsortedArray,lengthOfUnsortedArray);*/
363 
364         cout<<endl;
365         cout<<"please enter the identifier of the program to test (end with ctrl+z): "<<endl;
366     }
367 
368     delete [] unsortedArray;
369 }
370 
371 //使用脚手架的测试程序
372 int main(void)
373 {
374     TestDriver();
375     return 0;
376 }
377 
378 //不用脚手架的测试程序
379 //int main(void)
380 //{
381 //    int unsortedArray[MaxSize];
382 //    int len = 0;
383 //    DataType data;
384 //
385 //    cout<<"please enter the data of the array ,end with ctrl+z : "<<endl;
386 //    while (cin>>data)
387 //    {
388 //        unsortedArray[len++] = data;
389 //    }
390 //    cout<<"the unsorted array is :"<<endl;
391 //    DisplayArray(unsortedArray,len);
392 //
393 //    /*cout<<"Test InsertSortBasic..."<<endl;
394 //    InsertSortBasic(unsortedArray,len);
395 //    cout<<"the sorted array is :"<<endl;
396 //    DisplayArray(unsortedArray,len);*/
397 //
398 //
399 //    //cout<<"Test InsertSortImproved..."<<endl;
400 //    //InsertSortImproved(unsortedArray,len);
401 //    //cout<<"the sorted array is :"<<endl;
402 //    //DisplayArray(unsortedArray,len);
403 //
404 //    /*cout<<"Test InsertSortAdvacaed..."<<endl;
405 //    InsertSortAdvacaed(unsortedArray,len);
406 //    cout<<"the sorted array is :"<<endl;
407 //    DisplayArray(unsortedArray,len);*/
408 //
409 //    /*cout<<"Test QuickSortBasic..."<<endl;
410 //    QuickSortBasic(unsortedArray,0,len - 1);
411 //    cout<<"the sorted array is :"<<endl;
412 //    DisplayArray(unsortedArray,len);*/
413 //
414 //    /*cout<<"Test QuickSortImproved_1..."<<endl;
415 //    QuickSortImproved_1(unsortedArray,0,len - 1);
416 //    cout<<"the sorted array is :"<<endl;
417 //    DisplayArray(unsortedArray,len);*/
418 //
419 //    //cout<<"Test QuickSortImproved_2..."<<endl;
420 //    //QuickSortImproved_2(unsortedArray,0,len - 1);
421 //    //cout<<"the sorted array is :"<<endl;
422 //    //DisplayArray(unsortedArray,len);
423 //
424 //    cout<<"Test QuickSortAdvaced..."<<endl;
425 //    QuickSortAdvaced(unsortedArray,0,len - 1);
426 //    cout<<"the sorted array is :"<<endl;
427 //    DisplayArray(unsortedArray,len);
428 //    
429 //    return 0;
430 //}

测试结果:

(可以看到,同样的排序,最快的是采用函数QuickSortAdvaced(使用随机枢轴)与QuickSortImproved_2,标准库函数qsort与QuickSortImproved_1差不多,QuickSortBasic也比较快,且比QuickSortImproved_1差不多快一倍;

而插入排序很慢,最快的插入排序大概是最快的快速排序QuickSortAdvaced的200倍;

最慢的插入排序是最快的插入排序的100倍,可见即使是同样的算法,代码的编写对程序的效率也是很重要的,要从代码上进行优化)

the identifier of the program is :
InsertSortBasic : 11
InsertSortImproved : 12
InsertSortAdvacaed : 13
QuickSortBasic : 21
QuickSortImproved_1 : 22
QuickSortImproved_2 : 23
QuickSortAdvaced : 24

please enter the length Of UnsortedArray,MinRandomInt and MaxRandomInt :
100000 -1000000 1000000
please enter the identifier of the program to test (end with ctrl+z):
24
Test QuickSortAdvaced...
the average time cost per data is : 860 ns

please enter the identifier of the program to test (end with ctrl+z):
25
Test qsort...
the average time cost per data is : 2330 ns

please enter the identifier of the program to test (end with ctrl+z):
23
Test QuickSortImproved_2...
the average time cost per data is : 860 ns

please enter the identifier of the program to test (end with ctrl+z):
22
Test QuickSortImproved_1...
the average time cost per data is : 2020 ns

please enter the identifier of the program to test (end with ctrl+z):
21
Test QuickSortBasic...
the average time cost per data is : 1080 ns

please enter the identifier of the program to test (end with ctrl+z):
13
Test InsertSortAdvacaed...
the average time cost per data is : 164740 ns

please enter the identifier of the program to test (end with ctrl+z):
12
Test InsertSortImproved...
the average time cost per data is : 297960 ns

please enter the identifier of the program to test (end with ctrl+z):
11
Test InsertSortBasic...
the average time cost per data is : 3.66578e+006 ns

please enter the identifier of the program to test (end with ctrl+z):
^Z
请按任意键继续. . .

 

posted @ 2013-07-15 20:54  永不止步,永无止境  阅读(358)  评论(0编辑  收藏  举报