目录
分治法的定义:
将一个大规模问题分解成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、求解查找问题:
思路:采用类似快速排序的思想。与快速排序的区别为: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);
}
}
思路:先确定中位数在这两个数组中排第几,如:数组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、求解最大连续子序列和问题:
思路:将数组分成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;
}
}
本文深入探讨了分治法的定义、适用条件及求解过程,并通过快速排序、归并排序、查找问题和最大连续子序列和问题等实例,详细展示了分治法在解决实际问题中的应用。同时,提供了完整的Java代码实现,帮助读者理解算法逻辑。
2384

被折叠的 条评论
为什么被折叠?



