分而治之常见算法

分而治之

今天我们来学习一个基础算法策略——分而治之

分治和递归经常同时用于算法设计中。

什么是分治法?分而治之的思想就是将一个大问题分成一些小问题,再逐个击破。(这与递归不谋而合,但区别是:递归是方法,分治是思想)其中,这些子问题相互独立与原问题的结构相同但规模比原问题小,,所以与递归十分契合。

分治的步骤:分解(分解原问题成子问题)、求解(对子问题进行求解)、合并(将子问题合并成原问题)

归并排序

归并排序便是运用了分治思想

将待排元素分成两个子序列,对每个子序列再进行排序,最后再合并两个子序列

对n个元素进行归并排序:

(1)将n个元素分成各有n/2个元素的子序列

(2)对n/2个元素进行归并排序

(3)合并两个已排序的子序列

//输入数组A[1…n],数组下标left, right。平均时间复杂度O(nlogn)

void Merge(int a[],int left,int mid,int right)
{
 //临时数组用于存放初始未合并的两个子序列的元素
 int temp[MAXSIZE];
 int i,j,k;
 for(k=left;k<=right;k++)
     temp[k-left]=a[k];
 i=left;//第一个子序列的起始点
 j=mid+1;//第二个子序列的起始点
 for(k=left;k<=right;k++)
 {
     //如果第一个子序列的所有元素都已排完,将第二子序列中未排元素依次放入已排数组(即将两个序列合			并的目标数组)中
 	if(i>mid)
 	{
 		a[k]=temp[j-left];
 		j++;
 	  }
     //如果第二个子序列的所有元素都已排完,将第一子序列中未排元素依次放入目标数组尾部中
 	else if(j>right)
 	{
	    	a[k]=temp[i-left];
 		i++;
 	  }
     //在两个序列都未排完的情况下,当前第一子序列元素值大于当前第二子序列元素值,将较小的元素放入			目标数组尾部
 	else if(temp[i-left]>temp[j-left])
 	{
		    a[k]=temp[j-left];
		    j++;
		}
     //在两个序列都未排完的情况下,当前第二子序列元素值大于当前第一子序列元素值,将较小的元素放入			目标数组尾部
	    else
	    {
	    	a[k]=temp[i-left];
	    	i++;
	    }
 }		
}
void mergesort(int a[],int left,int right)
{
 if(left>=right)
	    return ;
	int mid=(left+right)/2;
 //将数组a分成两组{a[left]~a[mid]}和{a[mid+1]~a[right]},并进行归并排序
	mergesort(a,left,mid);
	mergesort(a,mid+1,right);
	Merge(a,left, mid, right);//合并		
}

最大子段和

最大子段和问题是在一个由n个整数组成的序列中找出最大的子段和。例如(-2,11,-4,13,-5,-2)的最大子段和为20

算法策略:

(1)分解。将数组分成左右两个长度相等的段,即A[1~n] -> A[1~n/2] + A[n/2+1 ~ n]

此时最大子段和有三种情况:

  1. 最大子段在左,即A[1n/2]的最大子段和A[1n]的最大子段相等
  2. 最大子段在右,即A[n/2+1n]的最大子段和A[1n]的最大子段相等
  3. 最大子段跨越左右子段,即A[i~j]的和,其中 1 <= i <= n/2、n/2+1 <= j <= n

(2)情况处理:对于第一、二种情况利用递归可求。对于第三种情况而言,由于需要求出A[i, j]的和,可以在左子段中求出s1 = MAX(A[i, n/2]),从右子段中求出s2 = MAX(A[n/2+1, j]),s1+s2即为所求

(3)合并,在三种情况中取出最大

//输入数组A[1…n],数组下标0, n-1。平均时间复杂度O(nlogn)
int MaxSubSum(int *a, int left, int right)
{
    int sum = 0;
    if(left == right)
    {
        sum = a[right];
        return sum;
    }
    else
    {
        //分解
        int mid = left + (right-left)/2;
        int leftsum = MaxSubSum(a, left, mid);//情况1
        int rightsum = MaxSubSum(a, mid+1. right);//情况2

        //情况3
        int s1 = 0;
        int lefts = 0;
        for(int i = mid; mid>=left;mid--)
        {
            left +- a[i];
            s1 = max(s1, lefts);
		}

        int s2 = 0;
        int rights = 0;
        for(int i = mid+1;i<=right;i++)
        {
            rights += a[i];
            s2 = max(s2, rights);
        }
        sum = s1+s2;

        return max(max(sum, rightsum), leftsum);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值