分治算法基本思想

      在一个给定数组中查找最大值和最小值是一类常见的问题,也是解决其他一些算法的基础。

假设给定数组为a,数组中含有n个元素,一般的算法是在数组中进行直接查找,算法伪代码如下:

1. x←a[0]; y←a[0]

2. fori←2 to n

3.   if a[i]<x then x←a[i]

4.   if a[i]>y then y←a[i]

5. endfor

6. return(x,y)

上述代码在第3行和第4行涉及到元素的比较,每次循环进行2次比较,而循环的次数在算法第2行给出,为(n-2)+1=n-1次,因此,算法元素比较总次数为2(n-1)次。

现在采用分治的思想,假设数组的长度为2的整数幂,将数组分割成两半,分别为a[0…(n/2)-1]和a[n/2…n-1],在每一半中分别查找最大值和最小值,并返回这两个最小值中的最小值以及两个最大值中的最大值。

假设给定数组为a,数组的下标上界和下界分别为low和high,则其算法伪代码如下:

minmax(a,low,high)

1. ifhigh-low=1 then

2.   if a[low]<a[high] then return(a[low],a[high])

3.   else return (a[high],a[low])

4.   end if

5. else

6.   mid←|_(low+high)/2_|

7.   (x1,y1)←minmax(a,low,mid)

8.   (x2,y2)←minmax(a,mid+1,high)

9.   x←min{x1,x2}

10.  y←max{y1,y2}

11.  return (x,y)

12.endif

代码第1行high-low=1表示数组长度为1,此时执行第2行~第4行代码直接比较数组的两个元素,选出最大值和最小值,此为函数的递归终止条件;代码第7行和第8行是两个递归调用,分别在数组的下标范围[low,mid]和[mid+1,high]查找最小值和最大值,第9行比较两个最大值取其中较大者,第10行比较两个最小值取较大者。

代码的第2、9和10行涉及到元素的比较,第7、8行由于递归也产生元素比较,minmax算法的元素比较总次数为3n/2-2,优于直接比较的性能。

比较的代码如下:

#include <stdio.h>
#define M 10
typedef struct Compare{
	int max;
	int min;
}compare;     //声明一个结构体compare类型 
//声明递归函数find,用来寻找最大值和最小值,返回类型为compare,用以接收目标数组中的最大值和最小值 
compare find(int *num,int low,int high);
int count=0;
int main(void)
{
	int num[M]={21,25,49,16,25,6,78,1,-8,6};
	compare tmp = find(num,0,M-1);
	printf("max is %d\nmin is %d\ncount is %d",tmp.max,tmp.min,count);
	return 0;
}

compare find(int *num,int low,int high)
{
	compare tmp;   //compare类型的tmp变量用以接收本次比较重的最大值和最小值 
	if(high-low==1)  //当分治后的小数组只有两个元素的时候,直接比较并返回 
	{
		count++;
		if(num[high]>num[low])
		{
			tmp.max = num[high];
			tmp.min = num[low];
		}
		else
		{
			tmp.max = num[low];
			tmp.min = num[high];
		}
	}
	//当数组长度n不是2的整数幂时
	//会出现high==low的情况
	//此时最大值和最小值都为num[high]或num[low] 
	else if(high==low)  
	{
		tmp.max = num[high];
		tmp.min = num[low];
	}
	else
	{
		count+=2;
		int mid = (low+high)/2;
		compare front = find(num,low,mid);
		compare back = find(num,mid+1,high);
		tmp.max = front.max>back.max?front.max:back.max;
		tmp.min = front.min>back.min?back.min:front.min;
	}
	return tmp;
}

思考:

使用分治算法解决最大子数组和问题,问题描述如下:

给定一个整数序列S,找出S中的连续子序列,使得该子序列和最大,要求算法时间复杂性为Θ(nlogn)。例如:-2, 11, -4, 13, -5,-2; 结果为20: (11, -4, 13)。

提示:

假定要寻找子数组S[low…high]的最大子数组,使用分治法将数组分解成两个尽可能想相等的子数组,找到子数组中点mid,则S[low…high]中任何连续数组S[i…j]必然是一下三种情况之一:

l  完全位于S[low…mid]中,low≤i≤j≤mid

l  完全位于S[mid+1…high]中,mid<i≤j≤high

l  跨越中点mid,low≤i≤mid<j≤high

因此,S[low…high]的一个最大子数组所处的位置必然是三种情况之一。可以通过递归方法求解A[low…mid]和A[mid+1…high]的最大子数组,则剩下的问题就是求解跨越中点的最大子数组,然后在三种情况下选择最大者。

代码如下(我写的代码的时间复杂度超过预期的Θ(nlogn),如果侥幸被哪位大神看到,欢迎指点):

#include <stdio.h>
#define M 6

int count(int *num,int low,int mid,int high);
int search(int *num,int low,int high);

int Max=-1;
int main(void)
{
	int num[M]={-2,11,-4,13,-5,-2};
	printf("max is %d",search(num,0,M-1));
	return 0;
}

int search(int *num,int low,int high)
{
	int max=-1;
	if(high==low) max = num[high];
	else
	{
		int mid = (low+high)/2;
		int front = search(num,low,mid);
		int back = search(num,mid+1,high);
		int center = count(num,low,mid,high);
		max = front>back?(front>center?front:center):(back>center?back:center);
	}
	return max;
}

int count(int *num,int low,int mid,int high)
{
	int i=0,j=0,k=0,sum=0,max=-1;
	for(i=low;i<=mid;i++)
	{
		for(j=mid+1;j<=high;j++)
		{
			sum=0;
			for(k=i;k<=j;k++)
			{
				sum+=num[k];
			}
			max = max>sum?max:sum;
		}
	}
	return max;
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我要出家当道士

打赏是不可能,这辈子都不可能

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值