【Divide-and-conquer DC分治】

一、梵塔问题



Void Hanoi(int n,int A,int B,int C)
{
   if(n>0) {
         Hanoi(n-1,A,C,B);
         move(n,A,B);
         Hanoi(n-1,C,B,A);
         }
}

分析时间复杂度
M(1)=1
M(n)=2M(n-1)+1
= 2[2M(n-2)+1]+1
= 22M(n-2)+2+1
= 23M(n-3)+22+2+1
=……
=2iM(n-i)+2i-1+2i-2+……+2+1
=2iM(n-i)+2i - 1
suppose i= n -
Then M(n)= 2n-1 + 2n-1-1=2n-1

二、假硬币问题

一个包里有16个硬币,其中有一个假的,假的那个比正常的轻,有一个天平。
问:如何找到这个假硬币。

把硬币分成A,B 两堆,每堆8个,假的在轻的一堆。重复此步骤。

三、金块问题

一个包里有若干金块,给你一个称,用最小的比较次数求得最重块与最轻块。
当n<=2时,可以直接得到。
当n>2时,将其分为A和B
分别找到的A的最重块HA、最轻块LA;B的最重块HB、最轻块LB
则最重块为HA与HB中的最大值
最轻块为LA与LB中的最小值。

public class TEST {
	public static void main(String args[]) {
		int a[]= {65,120,100,60,130,100,190};
		int [] result=fun(a,0,a.length-1);
		System.out.println(result[0]);
		System.out.println(result[1]);
	}
	private static int[] fun(int[] a,int l,int r) {
		if(r-l+1<=2) {
			int res[]=new int[2];
			res[0]=a[l]<a[r]?a[l]:a[r];
			res[1]=a[l]<a[r]?a[r]:a[l];
			return res;
		}
		int res[]=new int[2];
		int A[]=fun(a,l,l+(r-l)/2);
		int B[]=fun(a,l+(r-l)/2+1,r);
		res[0]=A[0]<B[0]?A[0]:B[0];
		res[1]=A[1]>B[1]?A[1]:B[1];
		return res;
	}

}

四、分治

1. 把一个问题分解成两个或者更多的子问题
2. 递归的解决子问题
3. 通过组合这些解获得原始(较大)问题的解

时间复杂度分析:

五、例子一——二分查找

public class ER_FEN_Research {
package 算法;

public class ER_FEN_Research {
	public static void main(String args[]) {
		int a[]= {1,2,3,4,5,6,7,8,9,10};
		int result1=fun1(a,-1);
		System.out.println(result1);
		int result2=fun2(a,7,0,a.length-1);
		System.out.println(result2);
	}
	public static int fun1(int a[],int key) {
		 int l=0;
		 int r=a.length-1;
		 while(l<=r) {
			 int mid=(r+l)/2;
			 if(a[mid]<key)
				 l=mid+1;
			 else if(a[mid]==key)
				 return mid;
			 else if(a[mid]>key)
				 r=mid-1;
		 }
		return -1;
	}
   public static int fun2(int a[],int key,int left,int right) {
	   int mid=(left+right)/2;
	   if(left>right) {
		   return -1;
	   }
	   if(a[mid]==key)
		   return mid;
	   else if(a[mid]<key)
		   return fun2(a,key,mid+1,right);
	   else 
		   return fun2(a,key,left,mid-1);
	  }
}

分析时间复杂度:

六、例子——斯特拉森矩阵(Strassen’s matrix)乘法


怎么改进呢??
idea1:

这里对 T(n)=8T(n/2)+φ(n2)中的φ(n2)进行解释:

如图 ae+bg实际上是n2/4个加法,而不是一个加法
所以一共4*(n2/4)=n2个加法

idea2:即斯特拉森解法

一共是7个乘法

时间复杂度:
k=2,b=2, a=7。
7=a>bk=4
所以是

这种情况 T(n)=O(22.81)
比刚刚的算法要好。

理论上目前为止这个问题的最优解是O(22.376)

七、例子——棋盘问题

有一个n*n(n=2k)大小的棋盘,其中之一是一个特殊的网格。用L型板盖住这张棋盘。任意两个三格板不能重叠

解法:

令t (k) 为覆盖一个2k×2k 残缺棋盘所需要的时间。当k= 0时,s i z e等于1,覆盖它将花费常数时间d。当k > 0时,将进行4次递归的函数调用,这些调用需花费的时间为 4t (k-1 )。除了这些时间外, if 条件测试和覆盖3个非残缺方格也需要时间,假设用常数c 表示这些额外时间。可以得到以下递归表达式:

八、例子——归并排序

伪代码

Void MergeSort(type a[ ], int left, int right)
{

    if left<right  //at least two elements
     {
        find the middle index “mid”
        Recursive invoke itself from “left” to “mid”
        Recursive invoke itself from “mid” to “right”
        Merge two sorted subresult
     }
}
package 算法;

public class merge_sort {
	public static void main(String  args[]) {
		int a[]= {12,34,56,76,89,100,44,37,1,3,56,70};
		mergeSort(a,0,a.length-1);
		for(int i=0;i<a.length;i++) {
			System.out.println(a[i]);
		}
		
		
		
	}
	public static void mergeSort(int a [],int left ,int right) {
		if(left<right) {
			int mid=(left+right)/2;
			mergeSort(a,left ,mid);
			mergeSort(a,mid+1,right);
			merge(a,left,mid,right);
			
			
		}
	}

	private  static void merge(int[] a, int left, int mid, int right) {
		// TODO Auto-generated method stub
		int b[]=new int[right-left+1];//临时空间
		int l1=left;
		int l2=mid+1;
		int r1=mid;
		int r2=right;
		int k=0;
		int i=l1,j=l2;
		while(i<=r1&&j<=r2) {
			if(a[i]<a[j]) {
				b[k]=a[i];
				i++;
				
			}
			else {
				b[k]=a[j];
				j++;
			}
			k++;
			
			
		}
		
		while(j<=r2) {
			b[k]=a[j];
			j++;
			k++;
		}
		while(i<=r1) {
			b[k]=a[i];
			i++;
			k++;
		}
		//复制部分
		for(int p=0;p<right-left+1;p++) {
			a[left+p]=b[p];
		}
		
		
	}
	

}

分析时间复杂度
合并长度为n1和n2的数组最多需要比较n1+n2-1次
所以,归并排序在最坏情况时间复杂度:

平均情况≈最坏情况
空间复杂性:需要额外的N个位置

九、例子——快排

步骤:
(Divide): take a[p] as pivot. Divide a[p:r] into a[p:q-1], a[q] and a[q+1:r].
(conquer):Recursive quicksort to a[p:q-1] and a[q+1:r].

伪代码:

void QuickSort(Type a[ ], int p,int r)
{
     if(p<r){
     int q=Partition(a,p,r);
     //Partition函数负责将a进行一次分割,返回分割元素的位置
     QuickSort(a,p,q-1);//对左半段排序
     QuickSort(a,q+1,r);//对右半段排序
     }
} 
public class fast_sort {
	public static void main(String  args[]) {
		int a[]= {12,34,56,76,89,100,44,37,1,3,56,70};
		fastSort(a,0,a.length-1);
		for(int i=0;i<a.length;i++) {
			System.out.println(a[i]);
		}
		
		
		
	}

	public static void fastSort(int[] a, int i, int j) {
		// TODO Auto-generated method stub
		if(i<j) {
			int p=partition(a,i,j);
			//System.out.println(p);
			fastSort(a, i, p-1);
			fastSort(a, p+1, j);
			
			
		}
	}

	private static int partition(int[] a, int left, int right) {
		// TODO Auto-generated method stub
		int i=left;
		int j=right+1;
		int x=a[i];
		while(true) {
			while(i<a.length-1&&a[++i]<x);
			while(a[--j]>x);
			if(i>=j) break;
			int temp=a[i];
			a[i]=a[j];
			a[j]=temp;
			
			
		}
		a[left]=a[j];
		a[j]=x;
		
		
		return j;
	}

	
	

}

时间复杂度:
Best case: split in the middle — Θ( n log n)
Worst case: sorted array! — Θ( n2)
Average case: random arrays — Θ( n log n)

没有比较排序算法运行得比nlogn好。

十、例子——线性时间选择

选择n个元素中第i小的元素

idea1:先排序再选择

idea2:随机选择分治算法

package 算法;

public class random_select {
	public static void main(String  args[]) {
		int a[]= {12,34,56,76,89,100,44,37,1,3,56,70};
		int r=RandomizedSelect(a,0,a.length-1,5);
		System.out.println(r);
	}
	
	
	public static int  RandomizedSelect(int[] a, int i, int j, int k) {
		// TODO Auto-generated method stub
		if(i==j)return a[i];
		int p=randomizedPartition(a,i,j);
		int m=p-i+1;
		if(k<=m) return RandomizedSelect(a,i,p,k);
		else return RandomizedSelect(a,p+1,j,k-m);
	 }


//和快排中分块代码一样的。
	private static int randomizedPartition(int[] a, int left, int right) {
		// TODO Auto-generated method stub
		int i=left;
		int j=right+1;
		int x=a[i];
		while(true) {
			while(i<a.length-1&&a[++i]<x);
			while(a[--j]>x);
			if(i>=j) break;
			int temp=a[i];
			a[i]=a[j];
			a[j]=temp;
	   }
		a[left]=a[j];
		a[j]=x;
	    return j;
	}
}

时间复杂度:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值