分治的算法设计思想

      分治(Divide and Conquer)是递归程序设计当中,一种非常重要的算法设计思路。利用它我们可以设计出许多高效的算法,例如排序、查找、大数乘积、矩阵乘法以及语法分析等等。它的核心思想是将一个复杂的问题分解成类型相同且相互独立的小问题,然后将小问题的解汇总,最终解决大问题。

      分治法的一般步骤

      使用分治法进行算法设计的一般步骤如下:

      1. 将问题分为子问题。这些子问题与原来问题的类型相同,但是规模更小;每个子问题的规模通常是相同的。(Divide

      2. 递归的解决这些子问题。(Conquer

      3. 恰当地结合这些子问题的解。(Combine

      在这些步骤当中,“Conquer”这个部分大多数时候是较为简单的,只需要递归地调用解决问题的函数自己就可以了。而“Divide”和“Combine”这两个部分,则很多时候是一个需要进行仔细设计的内容。

      分治法的具体例子

      归并排序(Merge Sort)是一个使用分治法的思路,进行算法设计的经典例子。这里以它为例子,对分治法的思想进行介绍。这里先把解决问题的思路用进行中文表述:

      1. 将原始的数列分成数量相等的左右两个部分。(Divide

      2. 对左右两个部分进行归并排序。(Conquer

      3. 将左右两个部分的排序结果进行合并,得到最终排序好的数列。(Combine

      将上面的内容改写成伪代码,可以表述为:

sortedArray = mergeSort(array)

// array : input array of length n
// sortedArray : sorted output array of length n

if 1 == n
sortedArray = array;
return sortedArray;
end

// Divide
leftPart = array(1...n/2);
rightPart = array(n/2+1...n);

// Conquer
leftPartSorted = mergeSort(leftPart);
rightPartSorted = mergeSort(rightPart);

// Combine
sortedArray = merge(leftPartSorted, rightPartSorted);

return sortedArray;

      在这个例子当中,我们可以看到“Divide”和“Conquer”这两个部分是相对简单的,而“Combine”这个部分则是一个需要我们考虑的问题,因为它的设计将决定整个算法的时间复杂度。所以现在需要解决的问题变为,如何高效地将两个有序数列合并成一个新的有序数列的问题。下面看一下我们怎样使用O(n)的时间复杂度来解决这个问题。

C = merge( A, B )

// A : input array of length nA
// B : input array of length nB
// C : output array of length n = nA + nB

i = 1;
j = 1;

for k = 1 to n 
    if A(i) < B(j)
	    C(k) = A(i);
		i++;
	else
	    C(k) = B(j);
		j++;
	end
end

return C;

      由于整个问题被分成了log2(n)层,每一层合并需要的时间复杂度为O(n),所以整个算法的时间复杂度便为O(nlog2(n))了,也可以等价简写为O(nlog(n))。下面是一个使用归并排序,解决具体问题的示意图:


      分治法的算法复杂度分析

      假设分治法“Divide”部分将规模n的问题分成了a个子问题,每个问题的规模为“n/b”,“Combine”子问题结果的时间复杂度为O(n^d),那么整个问题的时间复杂度会是怎么样呢。


      一个叫Master theorem的定理给了我们下面一个估算时间复杂度的方法,这个可以使我们对设计的算法有一个宏观上的掌握。详细的推导过程可以根据上面的图来进行,比较容易。


     其中T(n)表示整个算法的时间复杂度。在归并排序中,a = 2, b = 2, d = 1,故符合第二行的情况, 时间复杂度为O(nlog(n))。

L型组件填图问题 1.问题描述 设B是一个n×n棋盘,n=2k,(k=1,2,3,…)。用分治法设计一个算法,使得:用若干个L型条块可以覆盖住B的除一个特殊方格外的所有方格。其中,一个L型条块可以覆盖3个方格。且任意两个L型条块不能重叠覆盖棋盘。 例如:如果n=2,则存在4个方格,其中,除一个方格外,其余3个方格可被一L型条块覆盖;当n=4时,则存在16个方格,其中,除一个方格外,其余15个方格被5个L型条块覆盖。 2. 具体要求 输入一个正整数n,表示棋盘的大小是n*n的。输出一个被L型条块覆盖的n*n棋盘。该棋盘除一个方格外,其余各方格都被L型条块覆盖住。为区别出各个方格是被哪个L型条块所覆盖,每个L型条块用不同的数字或颜色、标记表示。 3. 测试数据(仅作为参考) 输入:8 输出:A 2 3 3 7 7 8 8 2 2 1 3 7 6 6 8 4 1 1 5 9 9 6 10 4 4 5 5 0 9 10 10 12 12 13 0 0 17 18 18 12 11 13 13 17 17 16 18 14 11 11 15 19 16 16 20 14 14 15 15 19 19 20 20 4. 设计与实现的提示 对2k×2k的棋盘可以划分成若干块,每块棋盘是原棋盘的子棋盘或者可以转化成原棋盘的子棋盘。 注意:特殊方格的位置是任意的。而且,L型条块是可以旋转放置的。 为了区分出棋盘上的方格被不同的L型条块所覆盖,每个L型条块可以用不同的数字、颜色等来标记区分。 5. 扩展内容 可以采用可视化界面来表示各L型条块,显示其覆盖棋盘的情况。 经典的递归问题, 这是我的大代码, 只是本人很懒, 不想再优化
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

理想主义帕鲁

感恩有你,日久弥新。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值