分治法

分治法
分治,分而治之也。将原问题划分成多个规模较小结构和原问题相似的子问题,分别递归解决这些子问题,然后将所有子问题的解合并即可得到原问题的解。 Divide:将原问题划分成一系列子问题 Conquer:分别递归解决子问题,如果子问题足够小,则可直接解决。 Combine:将子问题的结解合并一起成为原问题的解。
  	很多时候算法的设计都要靠分治策略。比如归并排序,也叫合并排序。

来看一下二路归并。
	Divide:将n个输入序列划分为两个n/2个元素的子序列
	Conquer:用MergeSort递归解决这两个字序列
	Combine:将这两个已排序的有序序列合并为一个完全排序的序列
	源代码

void MergeSort(int array[],int start,int end )
{
	int mid;
	if(start<end)            //必须要加以判断。如果只剩一个则直接返回
	{									
		mid=start+(end-start)/2;    //为了防止溢出
		MergeSort(array,start,mid);
		MergeSort(array,mid+1,end);
		Merge(array,start,mid,end);
	}
}


	举个例子
	初始序列是 2  8  7  1  3  5  6  4  

void Merge(int array[],int start,int mid,int end)
{						  ///合并有序的array[start-mid]与array[mid+1-end]
	int step;	
	for(int i=start,j=mid+1;i<j&&j<=end;)  
	{			  第一序列是i至j-1;第二序列是j至end;初始化j=mid+1
		while(i<j&&array[i]<=array[j])  
		{			  //这些元素位置将保持不变,因为它小于第二序列的初值

			i++;					
		}
		step=j-i;  	  //此时step就是序列一的长度,也是要移位的位数
		while(j<=end&&array[j]<=array[i])  
		{						//如果这些元素小于此时第一序列的初值//则和第一序列整体交换,因为都是有序的
					
			j++;   						 //j有变化
		}
		LeftShift(array,i,j-1,step); //整体向左循环移位。注意此时j的值
		i=j-step;    				//i继续向前 i=i+(j-i-step) 继续迭代
	}

}

	这个算法是原地排序,较利用辅助数组的算法难一些,稍加理解即可弄明白。
	i之前的元素位置不变,j之后的未知。i--j-1元素要调整,移位而已。
	例子
	3   4   6  7 和 2   5  8   9


	中间函数实现

void Reverse(int array[],int start,int end) //序列逆序
{

	int temp;
	for(int i=start,j=end;i<j;i++,j--)
	{
		temp=array[i];
		array[i]=array[j];
		array[j]=temp;
	}
}
void  LeftShift(int array[],int lo,int hi,int x) 	
{					//将序列向左循环移位x个位置
	Reverse(array,lo,lo+x-1); 	//将前X个元素逆序
	Reverse(array,lo+x,hi);  //将后N-X个元素逆序array[x]是第x+1个元素
	Reverse(array,lo,hi);
}


	时间复杂度T(n)=2T(n/2)+O(n)=O(nlgn) 空间复杂度O(1)。|| 别的算法有时候也会O(n)。

	递归树好比是最优划分。


	不仅仅是MergeSort,很多算法都应用分治策略。

Power a number problem  乘方问题:给你一个数,整数或者浮点数X,求X^n X^n=X*X*X*X*X*X*X*X*X*X.....X*X*X*X  如何求得?蛮力?那会有N-1次乘法的出现,会很费时间的。
	这时候就可以想到用分治法。 X^n=X^n/2*X^n/2=(X^n/4*X^n/4)*(X^n/4*X^n/4)=(X*X)*(X*X)*...*(X*X) 
	易知 T(n)=2T(n/2)+O(n)=O(nlgn),即便n是奇数也一样道理。

Fibonaci数列
	又称黄金分割数列。0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
	如果设F(n为该数列的第n项(n∈N+): 显然这是一个线性递推数列。
	很容易想到递归,是不是,多简单啊。
int Fibonaci(int n)
{
  if (n==0) 
  return 0;
  if (n==1) 
  return 1;
  if (n>1) 
  return Fibonaci(n-1)+Fibonaci(n-2);
}

	看起来时间复杂度不是很高 T(n)=T(n-1)+T(n-2)+O(1)+O(1)  其实T(n)是指数级的复杂度T(ƞ^n),ƞ=(√5+1)/2,因为递归有很多重复的运算。
	比如F(4)=F(3)+F(2)=F(2)+F(1)+F(1)+F(0)=F(1)+F(0)+F(1)+F(1)+F(0)=3。这个可以通过备忘录法,自底向上解决。用一个一维数组保存就搞定了。
其实有这样一个公式 
这个可以证明的。这样就和乘方问题一样了。很容易得到Fn的值了。时间复杂度也是O(nlgn)。

矩阵乘法 A,B都是N*N的矩阵,相乘得到C。 容易想到的是  这样3个循环嵌套,T(n)=O(n^3) 换个思路,我们可以利用分治策略,将矩阵分块。将一个矩阵分成4个小矩阵,这样A,B中都有4个小矩阵,这样相乘的次数就会减少很多。   a b c d e f g h 都是n/2*n/2的小矩阵。 →r=ae+bg;  s=af+bh;    t=ce+dg;   u=cf+dh; 这样8次递归,4次求和解决 T(n)=8(n/2)+(n^2)=O(n^3) 时间复杂度没有改变,但是相乘次数减少了。 目前最优的解决办法的复杂度是T(n)=O(n^2.376)

VTST布局 假设芯片是一个二叉树,要把它放在集成电路上,希望占据的空间较小。求最小占据空间面积。 H(n)=H(n/2)+O(1)=O(lgn)    W(n)=2W(n/2)+O(1)=O(n) 则S(n)=H(n)*W(n)=O(nlgn),浪费了不少的空间。总共N个节点,却占据了nlgn的空间。 有没有好的方法可以使分布更加的均匀一些呢,当然有。如何求得? 可以使其变成O(n)。这个需要高度和宽度的同时减少。
	同样利用分治策略

	此时W(n)=H(n)=L(n)=2L(n/4)+O(1)
	L(n)=O(√n) 则S(n)=O(n)   Very Cool!!!
	转载请注明出处http://blog.csdn.net/sustliangbo/article/details/9290161


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值