分治法与归并排序
分治法基本概念:
分治法是计算机科学中一种很重要的算法,分治法的核心理念就是把一个复杂的问题分成一个个规模较小的问题,然后对这些小问题各个击破。
“分治”是一种思想,它不涉及具体的算法,大多数情况下分治都是靠递归来达到效果的。
分治法特征:
分治法的几个特征:
-
该问题的规模缩小到一定的程度就可以容易地解决;
-
该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;(前提:这条特征是应用分治法的前提,同时也体现了递归的特征)
-
利用该问题分解出的子问题的解可以合并为该问题的解;(关键:能不能用分治法,关键就在于具不具备这个特征,如果前两条特征都具备,而这一条不具备可以考虑使用动态规划和贪心算法)
-
该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。(效率:如果子问题不是相互独立的,这样虽然可以使用分治法,但是效率上却会则扣,此时可以去考虑一下动态规划)
分治法基本步骤:
分治法在每一层递归上都有三个步骤:
step1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
step2 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
step3 合并:将各个子问题的解合并为原问题的解。
归并排序思想:
应用分治法的最经典的问题之一就是归并排序,这也是小编当时在学习数据结构的时候,没太弄懂的一个排序算法,所以今天抽出空来,好好研究一下这个算法,并与大家共同分享一下。
归并排序的是一个典型的牺牲空间换取时间的算法,这在算法种也是比较常见的情况。
归并排序的时间复杂度是O(nlogn)
归并排序相对于选择排序和插入排序有一些不足之处:
隐含在渐进符号前面的常量因子比另外两个算法的渐进符号前面的常量因子的值大。当然,一旦数组规模n变得非常大,那么这个常量因子也就没那么重要了。
归并排序不是原址的:它必须将整个输入数组进行完全的拷贝。而选择排序和插入排序在任何时间仅仅拷贝一个数组项而不是对所有数组项都进行拷贝。如果空间非常宝贵,那么使用归并排序不是一个好的选择。
我们以排列书架上的书籍为例,假设每本数据都有对应的标号,现在我们应用分治法的三步骤去考虑如何解决这个排序问题:
分解:通过找到位于p和r中间位置的数字q对问题进行分解。正如使用二分查找寻找中间点那样,将p和r相加,然后除以2,并向下取整。
解决:对分解步骤得出的两个子问题的书进行递归排序,对从位置p到位置q的书籍进行递归排序,且对从位置q+1到位置r的书籍进行递归排序。
合并:将从位置p到q的排序好的书籍和从位置q+1到r的排序好的书籍进行合并,使得从位置p到位置r的书籍排好序。
注意:当少于两本书籍需要排序(也就是p>=r)时,基础情况就会发生,因为不包含的书籍或者只拥有一本书的书籍已经是排好序的。
归并排序伪代码:
为了将这个观点转换成对数组进行排序,从位置p到位置r的书对应于子数组A[p...r]。下面是归并排序程序,它会调用一个程序MERGE(A,p,q,r),该程序会将排好序的子数组A[p..q]和A[q+1,..r]合并为单一的排好序的子数组A[p .. r]
程序 MERGE=SORT(A,p,q)
输入:
A:一个数组。
p,r: A的某个子数组的开始索引和末尾索引
结果:子数组A[p .. r]中的元素按照非递减顺序排序。
如果p>=r,那么子数组A[p .. r]至多有一个元素,因此它一定是有序的。无需执行任何操作即可返回。
否则,执行如下操作:
将q赋值为⌊(p+r)/2⌋。
递归调用MERGE-SORT(A,p,q)
递归调用MERGE-SORT(A,q+1,r)
调用MERGE(A,p,q,r)
程序 MERGE(A,p,q,r)
输入:
A:一个数组
p,q,r:关于数组A的索引。假定每个子数组A[p .. q]和A[q+1 .. r]均是有序的。
结果:子数组A[p .. r]包含初始时刻在A[p .. q]和A[q+1..r]中的元素,但是现在整个数组A[p .. r]是有序数组。
令n1取q – p + 1,n2取r-q。
令B[1.. n1] 以及C[1.. n2]为两个新数组
将A[p .. q]中的元素一次拷贝到B[1.. n1]中,将A[q+1 .. r]的元素依次拷贝到C[1.. n2]中。
令i和j均取1。
令k从p到r依次取值:
如果B[i]<=C[j],那么A[k]被赋值为B[i],同时将i自增1。
否则(B[i]>C[j]),A[