《算法设计与分析》之分治法

本文深入探讨了分治法的定义、适用条件及求解过程,并通过快速排序、归并排序、查找问题和最大连续子序列和问题等实例,详细展示了分治法在解决实际问题中的应用。同时,提供了完整的Java代码实现,帮助读者理解算法逻辑。

目录

分治法的定义:

分治法的适用条件:

分治法的求解过程:

分治法的具体应用案例:

1、快速排序和归并排序:

2、求解查找问题:

3、求解最大连续子序列和问题:


分治法的定义:

将一个大规模问题分解成k个相互独立、规模较小且与原问题形式相同的子问题,递归的解决这些子问题,然后合并各个子问题的解,从而得到原问题的解。

分治法的适用条件:

1、问题的规模缩小到一定的程度就能很容易的解决。

2、问题可以分解成若干个相似且独立的小问题。

3、子问题的解可以合并为原问题的解。

分治法的求解过程:

1、分解成若干个子问题。

2、求解子问题。

3、合并子问题的解。

分治法的具体应用案例:

1、快速排序和归并排序:

快速排序定义:在待排序的n个元素中任取一个元素作为基准,把所有小于这个基准的元素放在它的前面,形成一个新的子序列,把所有大于这个基准的元素放在它的后面,形成一个新的子序列。再对这两个子序列继续上述操作,直到每个子序列中只有一个元素为止。

快速排序过程演示(每次以数组的第一个元素为基准):

排序前:7        3        5        9        4        2

第一趟排序:2        3        5        4        7        9

第二趟排序:2        3        5        4        7        9

第三趟排序:2        3        5        4        7        9

第四趟排序:2        3        4        5        7        9

快速排序代码展示:

package test;

public class Test {
	public static void main(String[] args) {
		int[] a= {7,3,5,9,4,2};
		sort(a,0,a.length-1);
		for(int i=0;i<a.length;i++)
			System.out.println(a[i]);
	}
	private static void sort(int[] nums,int L,int R) {
		if(L>=R)
			return ;
		int left=L,right=R;
		int p=nums[left];
		while(left<right) {
			while(left<right&&p<nums[right])
				right--;
			if(left<right) {
				nums[left]=nums[right];
				nums[right]=p;
				left++;
			}
			while(left<right&&p>nums[left])
				left++;
			if(left<right) {
				nums[right]=nums[left];
				nums[left]=p;
				right--;
			}
		}
		sort(nums,L,left-1);
		sort(nums,right+1,R);
	}
}

归并排序定义:先将整个数组看成n个长度为1的有序表,将相邻的k(k>=2)个有序子表成对归并,得到n/k个长度为k的有序子表;然后继续将这些有序子表归并,得到n/(k*k)个长度为k*k的有序子表,直到最后得到1个长度为n的有序表。

归并排序过程演示(取k=2):

排序前:7        3        5        9        4        2

第一趟排序:3        7        5        9        2        4

第二趟排序:3        5        7        9        2        4

第三趟排序:2        3        4        5        7        9

归并排序代码展示:

package test;

public class Test {
	public static void main(String[] args) {
		int[] a= {7,3,5,9,4,2};
		sort(a,0,a.length-1);
		for(int i=0;i<a.length;i++)
			System.out.println(a[i]);
	}
	private static void sort(int[] nums,int L,int R) {
		if(L>=R)
			return ;
		int mid=(L+R)/2;
		sort(nums,L,mid);
		sort(nums,mid+1,R);
		int[] temp=new int[(R-L+1)];
		int i=L,j=mid+1,k=0;
		while(i<=mid&&j<=R) {
			if(nums[i]<nums[j])
				temp[k++]=nums[i++];
			else
				temp[k++]=nums[j++];
		}
		while(i<=mid)
			temp[k++]=nums[i++];
		while(j<=R)
			temp[k++]=nums[j++];
		for(i=L,k=0;k<temp.length;i++,k++)
			nums[i]=temp[k];
	}
}

2、求解查找问题:

LeetCode第215题 找出数组中的第k个最大元素

思路:采用类似快速排序的思想。与快速排序的区别为:1、每趟排序都会丢掉一部分元素;2、找到所求元素立即停止排序。

完整代码展示:

package leetcode_7;

public class T_215 {
	public static void main(String[] args) {
		int[] a= {3,2,3,1,2,4,5,5,6};
		System.out.println(findKthLargest(a,4));
	}
	public static int findKthLargest(int[] nums, int k) {
		return sort(nums,0,nums.length-1,k);
	}
	private static int sort(int[] nums,int L,int R,int k) {
		if(L==R)
			return nums[L];
		int left=L,right=R,p=nums[left];
		while(left<right) {
			while(left<right&&p>nums[right])
				right--;
			if(left<right) {
				nums[left]=nums[right];
				nums[right]=p;
				left++;
			}
			while(left<right&&p<nums[left])
				left++;
			if(left<right) {
				nums[right]=nums[left];
				nums[left]=p;
				right--;
			}
		}
		if(left==k-1)
			return nums[k-1];
		else if(left>k-1)
			return sort(nums,L,left-1,k);
		else
			return sort(nums,left+1,R,k);
	}
}

LeetCode第4题 寻找两个正序数组的中位数

思路:先确定中位数在这两个数组中排第几,如:数组1的长度为5,数组2的长度为8,则中位数排在第7位;数组1长度为5,数组2长度为7,则中位数排在第6位和第7位中间,故应该先求出这两个数,然后求均值。然后写一个求两个有序数组第k小元素的函数。

完整代码展示:

package leetcode_7;

import java.util.Arrays;

public class T_4 {
	public static void main(String[] args) {
		int[] a= {1,2,5,8,0};
		int[] b= {3,4,6,9,12};
		System.out.println(findMedianSortedArrays(a,b));
	}
	public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
		int m=nums1.length,n=nums2.length;
		int k=(m+n)/2;
		if((m+n)%2==1)
			return	f(nums1,m,nums2,n,k+1);
		return	(f(nums1,m,nums2,n,k)+f(nums1,m,nums2,n,k+1))/2;
	}
	private static double f(int[] a,int al,int[] b,int bl,int k) {
		if(al>bl)//f函数用于找两个有序数组中第k小的元素
			return f(b,bl,a,al,k);
		if(al==0)
			return b[k-1];
		if(k==1)
			return a[0]<=b[0]?a[0]:b[0];
		int na=al>=k/2?k/2:al;
		int nb=k-na;
		if(a[na-1]==b[nb-1])
			return a[na-1];
		else if(a[na-1]<b[nb-1]) {
			int[] temp=new int[al-na];
			for(int i=na,j=0;i<al;i++,j++)
				temp[j]=a[i];
			return f(temp,al-na,b,bl,k-na);
		}
		else {
			int[] temp=new int[bl-nb];
			for(int i=nb,j=0;i<bl;i++,j++)
				temp[j]=b[i];
			return f(a,al,temp,bl-nb,k-nb);
		}
	}
}

3、求解最大连续子序列和问题:

LeetCode第53题 最大子数组和

剑指 Offer 42 连续子数组的最大和

思路:将数组分成3部分,从左半部分的最大和,右半部分的最大和和从中间向两边延伸的最大和中选取最大值即为答案。而左半部分求最大和与右半部分求最大和又可以各自分成3部分,一直递归下去,直到数组中只有一个值,若为正数则直接返回该值,否则返回0。最开始分组之前要保证原始数组中的元素不全为负数。

完整代码展示:

package leetcode_7;

public class T_O_42 {
	public static void main(String[] args) {
		int[] a= {-2,1,-3,4,-1,2,1,-5,4};
		System.out.println(maxSubArray(a));
	}
	public static int maxSubArray(int[] nums) {
		int max=nums[0];
		for(int x:nums)
			if(max<x)
				max=x;
		if(max<0)
			return max;
		return maxSum(nums,0,nums.length-1);
	}
	private static int maxSum(int[] nums,int L,int R) {
		if(L==R)
			return nums[L]>0?nums[L]:0;
		int mid=(L+R)/2;
		int leftSum=maxSum(nums,L,mid);
		int rightSum=maxSum(nums,mid+1,R);
		int midSum=0,count=0;
		for(int i=mid;i>=L;i--) {
			count+=nums[i];
			midSum=Math.max(count, midSum);
		}
		count=midSum;
		for(int i=mid+1;i<=R;i++) {
			count+=nums[i];
			midSum=Math.max(count, midSum);
		}
		int max=Math.max(leftSum, rightSum);
		max=Math.max(max, midSum);
		return max;
	}
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值