数据结构与算法(5)减治法

减治法(reduceandconquermethod)在将原问题分解为若干个子问题后,利用了原问题的解与子问题的解之间的关系,
这种关系通常表现为:
(1)原问题的解只存在于其中一个较小规模的子问题中;
(2)原问题的解与其中一个较小规模的解之间存在某种对应关系。

由于原问题的解与较小规模的子问题的解之间存在这种关系,
所以,只需求解其中一个较小规模的子问题就可以得到原问题的解,无须对子问题的解进行合并。

因此,严格地说,减治法应该是一种退化了的分治法。

减治法主要有以下三种类型:

(1)减常量

算法每次迭代总是从实例规模中减去一个相同的常量,一般来说,这个常量等于1。
直接插入排序、拓扑排序等都是减1技术的应用实例。
在这里插入图片描述

(2)减常数因子

算法每次迭代总是从实例规模中减去一个相同的常数因子,一般来说,这个常数因子等于2(即减半)。
折半查找、平衡二叉树的查找、B树的查找、堆调整等都是减半技术的应用实例。

在这里插入图片描述


(3)减可变规模

算法每次迭代时减去的规模都是不同的。
欧几里得算法、二叉排序树的查找等都是减可变规模的应用实例。

算法设计实例——假币问题

【问题】 在n枚外观相同的硬币中有一枚是假币,并且已知假币较轻。
可以通过一架天平来任意比较两组硬币,从而得知两组硬币的重量是否相同,或者哪一组更轻一些。
假币问题是要求设计一个高效的算法来找出这枚假币。

【想法】 问题的解决是经过一系列比较和判断,最自然的想法就是一分为二,也就是把n枚硬币分成两组,每组有└n/2」枚硬币,如果n为奇数,就留下一枚硬币,然后把两组硬币分别放到天平的两端。如果两组硬币的重量相同,那么留下的硬币就是假币;否则,用同样的方法对较轻的那组硬币进行同样的处理,因为假币一定在较轻的那组里。由于每次用天平比较后,只需解决一个规模减半的问题,所以,它属于减治算法。该算法在最坏情况下的时间性能满足如下递推式:

在这里插入图片描述

通用分治递推式的定理,得到T(n)=O(log2^n)。

实际上,减半不是一个最好的选择。考虑不是把硬币分成两组,而是分成三组,前两组有「n/3┐个硬币,其余的硬币作为第三组;将前两组硬币放到天平上,如果它们的重量相同,则假币一定在第三组中,用同样的方法对第三组进行处理;如果前两组的重量不同,则假币一定在较轻的那一组中,用同样的方法对较轻的那组硬币进行处理。
显然这个算法存在递推式:

这个递推式的解是T(n)=O(log3n)。这个减治法是将原问题一分为三,从而获得了更少的比较次数。在这里插入图片描述
在这里插入图片描述

设N枚硬币的重量存储在数组a[N]中,函数Coin实现假币问题的求解,参数low和high分别表示假币所在的数组下标范围,为避免传递数组参数,将a[N]设为全局变量。

程序如下:

#include <stdio.h>

constintN = 8;
int a[N] = {2, 2, 1, 2, 2, 2,2 ,2 };
int Coin(int low, int high, int n);

int main(void)
{
	int i = Coin(0, 7, 8);
	printf("第 %d 枚硬币是假币\n", i);
	return 0;
}

int Coin(int low, int high, int n)
{
	int i , num1, num2, num3;
	int add1 = 0, add2=0;
	
	if(n == 1)
		return low+1;
	if( n%3 == 0)
		num1 = num2 -n/3;
	num3 = n-num1-num2;
	
	for( i = 0; i<num1; i++)
		add1 = add1 + a[low+i];
	for(i = num1; i<num1 + num2; i++)
		add2 = add2 + a[low + i];
	if(add1 < add2)
		return Coin(low, low+num1 -1, num1);
	elseif(add1 > add2)
		return Coin(low+num1, low+num1+num2-1, num2);
	else
		Coin(low+num1+num2, high, num3);
}


在这里插入图片描述

发布了349 篇原创文章 · 获赞 74 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/Ciellee/article/details/105120856