分治算法的简单应用——选最大和最小

分治法在实际应用中是很常用的,分治算法一般会有两种情况:
第一种情况就是将值返回回来使用;第二种就是重点在过程,出口仅仅可能就是一个return;

本篇是介绍一个分治算法的简单应用:选最大和最小

1、选最大算法

这个算法,也是在接触编程语言的时候敲过的最多的算法。

选最大算法就是循环遍历一遍数组,然后将数组中的值和max进行比较。最后输出max,即是原问题的解。

具体代码分析就不再详细介绍了。
这里先写个伪码:
max<-a[1]
for i<-2 to n do
if max<a[i]
then max<-a[i]
return max

具体的代码实现是这样的:

public static void main(String[] args) {
		// TODO Auto-generated method stub
		int a[]=new int[11];
		Random random=new Random();
		for(int i=0;i<a.length;i++)
			a[i]=random.nextInt(101);  //随机生成11个0到100的数字
		System.out.println("生成的结果是:"+Arrays.toString(a));
	    int k=Findmax(a);
		}

//找最大数
	public static int Findmax(int a[]) {
		int maxi=0;
		for(int i=1;i<a.length;i++) {
			if(a[maxi]<a[i]) {
				maxi=i;
			}
		}
		return maxi;
	}

我们经过代码分析得出:数组从1到n-1遍历,每个遍历都要进行1次比较。最后选出最大值返回。因此得出T(n)=n-1。

2、选最大和最小

蛮力法

刚刚在上面介绍了选最大算法。而对于最小,我们也可以用那样的算法找到最小值。

也就是我们可以先找到最大值,然后将最大值去掉,然后再循环数组找到最小值
当然我们也可以这样做,直接将max和数组元素进行比较,如果元素比max大,则将a[i]给max;否则就说明这个元素可能会很小,就去将元素和min进行比较。最后找到max,min

因此编写代码得:

public static void main(String[] args) {
		// TODO Auto-generated method stub
		int a[]=new int[11];
		Random random=new Random();
		for(int i=0;i<a.length;i++)
			a[i]=random.nextInt(101);  //随机生成11个0到100的数字
		System.out.println("生成的结果是:"+Arrays.toString(a));
	    SelectmaxAndmin(a);
	}


	//找最大和最小的数
	public static void SelectmaxAndmin(int a[]) {
		if(a[0]>a[1]) {
			maxi=0;
			mini=1;
		}
		else {
			maxi=1;
			mini=0;
		}
			
		for(int i=2;i<a.length;i++) {
			if(a[maxi]<a[i])
				maxi=i;
			else if(a[mini]>a[i]) {
				mini=i;
			}
		}
		
		System.out.println("最大数是:"+a[maxi]+",最小数是:"+a[mini]);
	}
		
		System.out.println("最大数是:"+a[maxi]+",最小数是:"+a[mini]);
	}
对蛮力法进行分析

我们看到啊,一开始在对a[0],a[1]做比较的目的:减少一次在循环中的比较。
我们将2到n-1进入循环比较。我们考虑一种情况。假设a[0]就是最大值,那么是不是后面的从a[2]开始的这n-2个数都进入了循环体中else if进行比较。当然我们也能看到if肯定也进入了。那么我们分析出进入if的比较次数是n-2次,进入else if的也是n-2次。而一开始的判断是1次。那么我们分析出T(n)=n-2+n-2+1=2n-3

那么有没有一种方法进行改进算法呢,当然是有的

分组法

对于分组法的算法是这样的:
1、我们将数组里面的元素两两分组,进行比较。大的进入max数组里,小的进入min数组里
两两分组中,如果遇到孤立元素,则这个孤立元素不必进行分组
2、在max数组里和孤立元素中找出最大值,在min数组和孤立元素中找出最小值。找出的最大值和最小值就是问题的解

我们进行代码分析,我们能够看到是将数组里面的元素进行两两分组,因此我们就能得出max[n/2],min[n/2]

分组中是要两两进行比较,i=0,i<n-1,i+=2 a[i]和a[i+1]进行比较,谁大进入max里,谁小进入min里。为什么是i<n-1,这是因为最后一个一定是a[n-2]和a[n-1]的比较。刚刚好不会涉及到a[n]这种错误的表示

然后对于分好组后的遍历,我们要进行分类讨论。如果n是奇数,最后最大和最小都要加一次和孤立元素的比较;如果n是偶数,最后直接得出问题的解

我们看下代码:

public static void main(String[] args) {
		// TODO Auto-generated method stub
		int a[]=new int[11];
		Random random=new Random();
		for(int i=0;i<a.length;i++)
			a[i]=random.nextInt(101);  //随机生成11个0到100的数字
		System.out.println("生成的结果是:"+Arrays.toString(a));
	    SelectmaxAndmin2(a);
	    }


//分组代码
	public static void SelectmaxAndmin2(int a[]) {
		int num1=0,num2=0;
		int max[]=new int[a.length/2];
		int min[]=new int[a.length/2];
		
		int pmax=-0x3f3f3f3f;
		int pmin=0x3f3f3f3f;
		
		
		//分组
		for(int i=0;i<a.length-1;i+=2) {
			if(a[i]<a[i+1]) {
				max[num1++]=a[i+1];
			    min[num2++]=a[i];
			}
			else {
			    max[num1++]=a[i];
				min[num2++]=a[i+1];
			}
		}
		
		
		//遍历两个数组
		if(a.length%2!=0) {
			for(int i=0;i<max.length;i++) {
				if(pmax<max[i]) {
					pmax=max[i];
				}
		}
			if(pmax<a[a.length-1])
				pmax=a[a.length-1];
			
			for(int i=0;i<min.length;i++) {
				if(pmin>min[i]) {
					pmin=min[i];
				}
			}
			if(pmin>a[a.length-1])
				pmin=a[a.length-1];
	}
		
		else {
			for(int i=0;i<max.length;i++) {
				if(pmax<max[i]) {
					pmax=max[i];
				}
		}
			
			for(int i=0;i<min.length;i++) {
				if(pmin>min[i]) {
					pmin=min[i];
				}
			}
		}
		
		System.out.println("最大值是:"+pmax+",最小值是:"+pmin);
	}

我们对分组法进行分析:
对这种算法进行时间复杂度分析,就是分析比较次数
分组中的比较次数是n/2次
如果是奇数我们在对max和min数组和孤立元素进行遍历比较的时候,我们按照找最值2(⌈n/2⌉-1)(上取整是因为会多加一次和孤立元素进行比较)
最后得出了T(n)=n/2+2(⌈n/2⌉-1)=n/2+2⌈n/2⌉-2=⌈3n/2⌉-2

我们发现分组算法比蛮力算法的T(n)要低

分治算法

分治算法是这样的:
1、首先将数组一分为二为L1,L2
2、递归的在L1中寻找max1,min1
3、递归的在L2中寻找max2,min2
4、max=MAX(max1,max2)
5、min=MIN(min1,min2)
得出的max和min是问题的解

对于类似这样分治算法我们可以去考虑如下写法:
1、定划分,我们考虑将整体划分成两个规模较小的与原问题同性质的子问题。然后
else{划分代码+递归代码}
2、定出口:if(出口标志){出口代码(1、返回值初值类型2、返回出口,即直接写一个return;)}
3、归结解:将所有的解组合到一个大的集合。
1、我们将通过递归改变的小集合传到递归参数里。然后将这些小的集合组合到一个大集合里
2、我们直接传一个大集合到递归参数里,通过各种变换直接输出大集合,即大集合就是原问题的解
3、可以将大数组定义成静态类型,这样在递归变化时,可以时刻调用或改变大集合里面的内容,最后将大集合输出

这里就不再进行详细分析,大家可以尝试分析下:

    public static int fmax=-0x3f3f3f3f;  //记录最大值
	public static int fmin=0x3f3f3f3f;  //记录最小值
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int a[]=new int[11];
		Random random=new Random();
		for(int i=0;i<a.length;i++)
			a[i]=random.nextInt(101);  //随机生成11个0到100的数字
		System.out.println("生成的结果是:"+Arrays.toString(a));
		SelectmaxAndmin3(a, 0, a.length-1);
		System.out.println("最大值:"+fmax+",最小值是:"+fmin);
	}

//分治法
	public static void SelectmaxAndmin3(int a[],int l,int r) {
		if(r==l) {
			fmax=Math.max(fmax, a[l]);
			fmin=Math.min(fmin, a[l]);
			return;
		}
		else if(l+1==r) {
			fmax=Math.max(Math.max(a[l], a[r]), fmax);
			fmin=Math.min(Math.min(a[l], a[r]), fmin);
			return;
		}
		else {
			int mid=(l+r)>>1;
			SelectmaxAndmin3(a, l, mid);
			SelectmaxAndmin3(a, mid+1, r);
		}
	}

最后我们再对分治法进行分析:
对于这个分析还是通过计量递归来分析
T(n)=2T(n/2)+2    (2是两次寻找max和min)
T(2)=1
通过迭代法能够分析出T(n)=3n/2 -2
我们能够分析出:分治算法和分组算法一样,T(n)是一样的值

尽管时间复杂度都是O(n),但是后两个方法却稍稍能提高效率

3、分治算法总结

分治算法就是将原问题划分成与原问题相同性质的子问题,直接划分注意尽量均衡。子问题通过计算得出的问题的解归结成原问题的解。算法实现要注意递归或迭代实现

分治算法的时间复杂度分析,给出关于时间复杂度函数的递推方程和初值。

分治算法的时间复杂度就是原问题规约成子问题中,原问题的工作量就是所有子问题工作量之和+规约和综合解的工作量之和

改进分治算法的途径就是:
1、减少子问题的个数(降低a)
2、增加预处理(降低f(n))

分治算法举例:
检索算法:二分检索
排序算法:快速排序、二分归并排序
选择算法:第k小问题
凸包问题以及快速傅里叶变换FFT算法

学分治算法是学它的思想和分治方法以及在代码中是如何应用、如何划分的

以上就是分治算法的介绍

        博学之,审问之,慎思之,明辨之,笃行之《礼记·中庸》
  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值