“自顶向下,逐步求精”在算法中的应用
“自顶向下,逐步求精”其实是一个很简单的概念,总的来说,就一句话:“将大问题分解为小问题”。
在这里,介绍一个利用“自顶向下,逐步求精”的算法:归并排序。
各位看官不要看到“归并”之类的算法名称就跑了,其实这是非常非常简单的而且便于理解的算法。
我们在VMatrix上面做过这一个题:
Merging Lists
Description
Write the following function that merges two sorted lists into a new sorted list:
void merge(const int list1[], int size1, const int list2[], int size2, int list3[])
wherelist1
andlist2
are sorted non-decreasingly,size1
andsize2
are the numbers of integers inlist1
andsize2
, respectively. For example, supposelist1
is{1, 5, 16, 61, 111}
, andlist2
is{2, 4, 5, 6}
. After invokingmerge(list1, 5, list2, 4, list3)
,list3
is{1, 2, 4, 5, 5, 6, 16, 61, 111}
.
由于这是程序设计(I)的作业,现在我们假设大家都已经会做这道题了,此处不放题解。我们要解决的是另外一道题,这道题的描述比上面这道简单得多:
将一个无序数组排序。
那么这是一个大家都耳熟能详的问题了,我们已经知道冒泡排序法和选择排序法。但是,这两种方法效率太低了。我们能否用到“自顶向下,逐步求精”的方法呢?
实际上是可以的。我们要得到一个有序的数列,只要将两个有序的数列合并为一个就可以了。
这两个有序的数列从哪里来?分别再用两个有序的数列合并为一个就可以了。
这四个有序的数列从哪里来?分别再用两个有序的数列合并为一个就可以了。
这八个有序的数列从哪里来?分别再用两个有序的数列合并为一个就可以了。
…………
如此循环往复,只到分解为只剩下一个数为止。此时有序的数列显然就是这一个数了,那么就可以开始合并,最终真正得到一个有序的数列。
理论上说,将一个数列分割,可以从任意点分割开。但是如果将分割点放在开头,则与冒泡排序无异,失去其优势。所以我们通常将分割点放在数列中间,此时该算法的时间复杂度即为O(n log n)(算法中,log 的底数为2,与数学中的 lg 不同)。
事实上,“自顶向下,逐步求精”在算法中的应用非常广泛,通常被称为“分治法”。分治法是其它很多算法的基础。算法是一个很奇妙的东西,值得好好研究。
感谢你看到这里。