五大算法之分治算法

一、基本思想

       当我们求解某些问题时,由于这些问题要处理的数据相当多,或求解过程相当复杂,使得直接求解法在时间上相当长,或者根本无法直接求出。对于这类问题,我们往往先把它分解成几个子问题,找到求出这几个子问题的解法后,再找到合适的方法,把它们组合成求整个问题的解法。如果这些子问题还较大,难以解决,可以再把它们分成几个更小的子问题,以此类推,直至可以直接求出解为止。这就是分治策略的基本思想。

二、二分法

       利用分治策略求解时,所需时间取决于分解后子问题的个数、子问题的规模大小等因素,而二分法,由于其划分的简单和均匀的特点,是经常采用的一种有效的方法,例如二分法检索。

三、解题步骤

分治法解题的一般步骤:
(1)分解,将要解决的问题划分成若干规模较小的同类问题;
(2)求解,当子问题划分得足够小时,用较简单的方法解决;
(3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。



四、应用场景

运用分治策略解决的问题一般来说具有以下特点:
1、原问题可以分解为多个子问题
这些子问题与原问题相比,只是问题的规模有所降低,其结构和求解方法与原问题相同或相似。
2、原问题在分解过程中,递归地求解子问题
由于递归都必须有一个终止条件,因此,当分解后的子问题规模足够小时,应能够直接求解。
3、在求解并得到各个子问题的解后
应能够采用某种方式、方法合并或构造出原问题的解。
不难发现,在分治策略中,由于子问题与原问题在结构和解法上的相似性,用分治方法解决的问题,大都采用了递归的形式。在各种排序方法中,如归并排序、堆排序、快速排序等,都存在有分治的思想。


五、示例

1)二分搜索

二分搜索的前提是“数据必须有序”,也就是先要排序好。

binarySearch.cpp

#include <stdio.h>

/// <summary>
/// 分治算法之二分法查找
/// </summary>
/// <param name="array">数组指针</param>
/// <param name="len">数组长度</param>
/// <param name="value">查询的值</param>
/// <returns>查询到的下标</returns>
int binarySearch(int *array, int len, int value)
{
	int low = 0;
	int high = len-1;
	//index : 将返回的下标
	int index = -1;

	while(low <= high)
	{
		//二分,得到中间位置
		int mid = (high + low)/2;

		//找到该数
		if (value == array[mid])
		{
			index = mid;
			break;
		}
		else if (array[mid] < value)
		{//值位于右边
			low = mid + 1;
		}
		else
		{//值位于左边
			high = mid - 1;
		}
	}

	return index;
}
/// <summary>
///  主函数
/// </summary>
/// <returns></returns>
int main(void)
{
	int array[] = {0,1,2,3,4,5,6,7,8,9};
	//len : 数组长度
	int len = sizeof(array)/sizeof(array[0]);
	int value = array[5];
	//index 查找位置
	int index = binarySearch(array, len,value);

	//index = -1 :未找到该值
	if (-1 != index)
	{
		printf("the value is %d\n",array[index]);
	}

	return 0;
}

2)大数乘法

大数乘法,实现原理就是用计算机模拟人工手动计算,比如:123 x 45,只要会123x5那么,123x4就是一样的计算原理。


BigDiv.cpp

//大数乘法  By:HHAIL 
//2015年1月26日13:49:27
#include <iostream>
#include <string>
using namespace std;

/// <summary>
/// 大数乘法
/// </summary>
/// <param name="num1">被乘数</param>
/// <param name="num2">乘数</param>
string BigDiv(string num1, string num2)
{
	//len1获得被乘数的长度,用于动态开辟缓存空间
	int len1 = num1.size();
	//len2乘数的长度
	int len2 = num2.size();
	//需要开辟缓存空间大小
	int len = len1+len2;
	//buf缓存计算数据
	int *buf = new int[len];
	//将buf数组内容全部初始化为0,你也可以用for循环
	memset(buf,0,sizeof(int)*(len) );
	
	//---------模拟乘法--------
	//--i比i--效率更高,t用于配合乘数偏移
	for (int it = 0,i = len2-1; i >= 0; --i,++it)
	{//i 循环为乘数,初始位置为最末尾
		for (int jt = 0,j = len1-1; j >= 0; --j,++jt)
		{//j 循环为被乘数,初始位置为最末尾
			//注意存储位置,buf数组,从后往前存放内容
			buf[len-1-it-jt] += ( (num1[j]-'0')*(num2[i]-'0') );
		}
	}
	
	//----------模拟进位--------
	for (int k = len-1; k > 0; --k)
	{
		if (buf[k] > 9)
		{
			//进位
			buf[k-1] += (buf[k] / 10); 
			buf[k] = (buf[k] % 10);
		}
	}

	//----过滤buf[]前面无用的0-----
	//index标识第一个不为0的位置
	int index = 0;
	while ( 0 == buf[index] )
	{
		++index;
	}

	//-----将数字转化为string,返回给main()----
	string result;	
	for (int x = index; x < len; ++x)
	{
		//string重载了+=,其效果就是直接追加在尾部
		result += (buf[x]+'0');
	}

	return result;
}
/// <summary>
/// 交换两数,使num1>num2
/// </summary>
/// <param name="num1"></param>
/// <param name="num2"></param>
void swap(string &num1, string &num2)
{
	string buf;
	if ( ( num1.size() <= num2.size() ) && (num1 < num2))
	{
		buf = num1;
		num1 = num2;
		num2 = buf;
	}
}

int main(void)
{
	string num1,num2;

	//输入两个大数,这里用C++string可以支持动态输入
	cin>>num1>>num2;
	swap(num1,num2);

	cout<<BigDiv(num1,num2)<<endl;

	return 0;
}

3)快速排序

快速排序,就三步:

1)选择一个基准Select

2)将该Select摆放在合适的位置

3)重复1、2步骤

注意:选择的基准(Select),这个是自己随便选择的。所谓合适的位置,就是要使该位置前面的数字全部小于(大于)Select,后面的数字全部大于(小于)Select,同时一旦确定了Select的摆放位置,以后永远不再挪动Select。比如:下图中选择第一个数字9为基准,那么就要把9摆放在合适的位置(使9前面的数字全部小于9,后面的数字全部大于9,但不一定要有序)。下图为一趟对9的快速排序,直接找到了9应该摆放的位置。


//快速排序 By HHAIL
//2015年1月26日14:10:47
#include <stdio.h>

void Qsort(int a[], int low, int high)
{
	int Select = a[low];          
	int i = low, j = high;    //确保low,high值不变

	if (i >= j)
		return;
	else
	{
		while (i < j)          //i < j 确保两个寻找下标不冲突
		{
			while (a[j] > Select && i < j)  //从右往左找比Select小的值
				--j;
			a[i] = a[j];                    //将该值放在上一步空出的位置
			while (a[i] <= Select && i < j) //从左往右找比Select大的值
				++i;
			a[j] = a[i];                    //将该值放在上一步空出的位置
		}
	}
	a[i] = Select;   //或者a[j] = Select; 此时i == j,将该数放入,此时Select已经排放好

	Qsort(a, low, i-1);   //先排左边,i-1和j-1是一样的
	Qsort(a, i+1, high);  //排右边
}

int main()
{
	int i, a[5] = {13,4,7,8,2};

	Qsort(a, 0, 4);
	for (i = 0; i < 5; ++i)
		printf("%d ",a[i]);

	return 0;
}

未完待续,示例更新中……

六、参考文献:

百度百科,分治算法,http://baike.baidu.com/view/1650802.htm

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值