分治法实例

分治法查找最大值和次大值

//求解最大和次大元素算法
#include <stdio.h>

// 宏, 求两个数的最大值, 可用函数代替
#define max(x,y) ((x)>(y)?(x):(y))

// 宏, 求两个数的最小值, 可用函数代替
#define min(x,y) ((x)<(y)?(x):(y))

// 可根据需要修改INF值
#define INF 99999		//表示最大的整数

// a: 保存数据的数组
// low: 要求最大和次大元素的所有元素的起始下标
// high: 要求最大和次大元素的所有元素的结束下标
// max1: 返回最大元素
// max2: 返回次大元素
// 函数作用:
//		找到a[low..high]的最大元素和次大元素, 分别用max1和max2返回
void solve(int a[],int low, int high,int &max1,int &max2)
{
	if(low == high)		//区间只有一个元素
	{
		max1 = a[low];
		max2 = -INF;
	}
	else if (low == high-1)	//区间只有两个元素
	{
		max1 = max(a[low], a[high]);
		max2 = min(a[low],a[high]);
	}
	else
	{
		int mid = (low + high) / 2;
		
		// 对左区间求解
		int lmax1, lmax2;
		solve(a, low, mid, lmax1, lmax2);		//左区间求lmax1和lmax2

		// 对右区间求解
		int rmax1, rmax2;
		solve(a, mid + 1, high, rmax1, rmax2);	//右区间求lmax1和lmax2

		// 综合解
		if (lmax1 > rmax1)// 【左区间最大值】大于【右区间最大值】
		{
			max1 = lmax1;// 最大值是【左区间最大值】
			max2 = max(lmax2, rmax1);//次大值是【左区间次大值】与【右区间最大值】之间【较大的那个】
		}
		else// 【左区间最大值】小于等于【右区间最大值】
		{
			max1 = rmax1;//最大值是【右区间最大值】
			max2  =max(lmax1, rmax2);//次大值是【左区间最大值】与【右区间次大值】之间【较大的那个】
		}
	}
}


int main()
{
	int a[] = {5, 2, 1, 4, 3};
	int n = sizeof(a) / sizeof(a[0]);
	int max1, max2;
	solve(a, 0, n - 1, max1, max2);
	printf("max1 = %d, max2 = %d\n", max1, max2);// 应为5和4

	return 0;
}

分治法查找第k小的元素

#include <stdio.h>

// a: 保存数据的数组
// s: 查找区间的起始下标
// t: 查找区间的结束下标
// 作用:
//    在a[s..t]中查找a的第k小的元素并返回
int QuickSelect(int a[], int s, int t, int k)
{  
	int i = s, j = t, tmp;
	if(s < t)
	{  
		// 作一次划分
		tmp = a[s];
		while ( i != j) 		 //从区间两端交替向中间扫描,直至i=j为止
		{  
			// 从右往左扫, 找到第一个小于基准a[s]的元素
			while(j > i && a[j] >= tmp) 
				j --;
			// 将该元素放到a[i]
			a[i] = a[j];		
			
			// 从左往右扫, 找到第一个大于基准a[s]的元素
			while(i < j && a[i] <= tmp) 
				i ++;
			// 将该元素放到a[j]
			a[j] = a[i];		
		}
		// 此时i==j, 基准元素放在此处, 小于等于基准的在左边, 大于等于基准的在右边
		a[i] = tmp; // 此时a[i]就是a的第i+1小的元素
		// 以上, 完成一次划分, 与快速排序中的划分相同

		// 如果该次划分的基准就是a[s, t]中第k小的元素
		if(k - 1 == i) 
			return a[i];// 返回
		else if(k - 1 < i) 
			return QuickSelect(a, s, i - 1, k);	//在左区间a[s..i-1]中查找a的第k小元素
		else 
			return QuickSelect(a, i + 1, t, k);	//在右区间a[i+1..t]中查找a的第k小元素		
				
	}
	else if(s == t && s == k - 1)	//区间内只有一个元素且为a[k-1]
		return a[k - 1];
}

int main()
{
	int a[] = {6, 3, 1, 5, 8, 9, 2, 7, 10, 4};
	int n = sizeof(a) / sizeof(a[0]);

	printf("数组a中第8小的元素为: %d\n", QuickSelect(a, 0, n - 1, 8));

	return 0;
}

折半查找递归算法

#include <stdio.h>

// a: 保存数据的数组
// low: 要查找的区间的起始下标
// high: 要查找的区间的结束下标
// 作用:
//	   在a[low..high]中查找值为k的元素
//	   找到, 则返回该元素下标
//	   找不到, 则返回-1(不可能的下标)
int BinSearch(int a[], int low, int high, int k) //拆半查找算法
{	
	int mid;
	if (low <= high)				//当前区间存在元素时
	{	
		mid = (low + high) / 2;		//求查找区间的中间位置
		if(a[mid] == k)			//找到后返回其物理下标mid
			return mid;

		if (a[mid] > k)			//当a[mid]>k时,在a[low..mid-1]中递归查找
			return BinSearch(a, low, mid - 1, k);
		else					//当a[mid]<k时,在a[mid+1..high]中递归查找
			return BinSearch(a, mid + 1, high, k);
	}
	else
		return -1;			//若当前查找区间没有元素时返回-1
}

int main()
{	
	int n = 10, i;
	int k = 6;
	int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	
	i = BinSearch(a, 0, n - 1, k);
	if (i >= 0)	
		printf("a[%d] = %d\n", i, k);//a[5] = 6
	else 
		printf("未找到%d元素\n", k);
	return 0;
}

折半查找非递归算法

#include <stdio.h>

// a: 保存数据的数组
// n: 数组a中元素个数
// 作用:
//	   在a[0..n-1]中查找值为k的元素
//	   找到, 则返回该元素下标
//	   找不到, 则返回-1(不可能的下标)
int BinSearch1(int a[], int n, int k) //拆半查找算法
{	
	int low = 0, high = n - 1, mid;
	
	while (low <= high)			//当前区间存在元素时循环
	{	
		mid = (low + high) / 2;		//求查找区间的中间位置
		if(a[mid] == k)			//找到后返回其物理下标mid
			return mid;
		if (a[mid] > k)			//继续在a[low..mid-1]中查找
			high = mid - 1;
		else					//a[mid]<k
			low = mid + 1;			//继续在a[mid+1..high]中查找
	}
	return -1;					//若当前查找区间没有元素时返回-1
}

int main()
{	
	int n = 10, i;
	int k = 6;
	int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	
	i = BinSearch1(a, n, k);
	if (i >= 0)	
		printf("a[%d] = %d\n", i, k);//a[5] = 6
	else 
		printf("未找到%d元素\n", k);
	return 0;
}

分治法求解棋盘覆盖问题

#include<stdio.h>
#define MAX 1025

//问题表示
int k;				// 棋盘大小
int x, y;			// 特殊方格的位置

//求解问题表示
int board[MAX][MAX];// 记录棋盘各个位置对应的骨牌序号
int tile = 1; // 记录当前要取的骨牌序号	

// tr: 当前处理的子棋盘左上角在原始棋盘上的行
// tc: 当前处理的子棋盘左上角在原始棋盘上的列
// dr: 特殊方格在原始棋盘上的行
// dc: 特殊方格在原始棋盘上的列
// size: 当前处理的子棋盘的尺寸(宽=高)
void ChessBoard(int tr, int tc, int dr,int dc, int size)
{   
	// 特殊情况
	// 只有一个位置, 且该位置一定是特殊方格
	// 无需放置L形骨牌
	if(size == 1) 
		return;		//递归出口

    int t = tile ++;//取一个L型骨牌,其牌号为tile
    int s = size / 2;//分割棋盘成4个象限(4个子棋盘)
    
	// 考虑左上角象限
    if(dr < tr + s && dc <tc + s)//特殊方格在此象限中
		// 递归调用, 解决这个象限
		ChessBoard(tr, tc, dr, dc, s);
    else//此象限中无特殊方格
    {	
		// 用t号L型骨牌覆盖右下角
		// 后续过程中, 该位置将被视为特殊方格
		board[tr + s - 1][tc + s - 1] = t;	

		//将右下角作为特殊方格继续处理该象限
		ChessBoard(tr, tc, tr + s - 1, tc + s - 1, s);				
    }

	//考虑右上角象限
    if(dr < tr + s && dc >= tc + s)// 特殊方格在此象限中   
		// 递归调用, 解决这个象限
		ChessBoard(tr, tc + s, dr, dc, s);		
    else//此象限中无特殊方格
    {	
		// 用t号L型骨牌覆盖左下角
		// 后续过程中, 该位置将被视为特殊方格
		board[tr + s - 1][tc + s] = t;

		//将左下角作为特殊方格继续处理该象限
		ChessBoard(tr, tc + s, tr + s - 1, tc + s, s); 				
    }

	// 处理左下角象限
    if(dr >= tr + s && dc < tc + s)//特殊方格在此象限中
		// 递归调用, 解决这个象限
		ChessBoard(tr+s,tc,dr,dc,s);  
    else//此象限中无特殊方格
    {   
		// 用t号L型骨牌覆盖右上角
		// 后续过程中, 该位置将被视为特殊方格
		board[tr + s][tc + s - 1] = t; 

		//将右上角作为特殊方格继续处理该象限
        ChessBoard(tr + s, tc, tr + s, tc + s - 1, s);
    }

	//处理右下角象限
    if(dr >= tr + s && dc >= tc + s)//特殊方格在此象限中
		// 递归调用, 解决这个象限
        ChessBoard(tr + s, tc + s, dr, dc, s); 
    else//此象限中无特殊方格
    {	
		// 用t号L型骨牌覆盖左上角
		// 后续过程中, 该位置将被视为特殊方格
		board[tr + s][tc + s] = t; 

		//将左上角作为特殊方格继续处理该象限
		ChessBoard(tr + s, tc + s, tr + s, tc + s, s); 				
    }
}

int main()
{
	k = 3;
	x = 1;
	y = 2;

	int size = 1 << k; //size=2^k
	ChessBoard(0, 0, x, y, size);

	for(int i = 0; i < size; i ++)
	{
		for(int j = 0; j < size; j ++)
			printf("%4d", board[i][j]);
		printf("\n");
	}

	return 0;
}

分治法求最大连续子序列和

//求解最大连续子序列和的算法
#include <stdio.h>

long max3(long a, long b, long c)				//求出三个long中的最大值
{	
	if(a < b) //用a保存a、b中的最大值
		a = b;								
	
	if(a > c)//比较返回a、c中的最大值 
		return a;							
	else 
		return c;  
}

//求a[left..high]序列中最大连续子序列和
long MaxSubSum(int a[], int left, int right)	
{	
	int i, j;
	// maxLeftSum: a[left..mid]中的最大连续子序列和
	// maxRightSum: a[mid+1..right]中的最大连续子序列和
	long maxLeftSum, maxRightSum;

	// maxLeftBorderSum: 从中心分隔线往左的最大连续子序列和
	long maxLeftBorderSum, leftBorderSum;
	// maxRightBorderSum: 从中心分隔线往右的最大连续子序列和
	long maxRightBorderSum, rightBorderSum;

	// 特殊情况
	if(left == right)						//子序列只有一个元素时
	{	
		if(a[left] > 0) 					//该元素大于0时返回它
			return a[left];
		else								//该元素小于或等于0时返回0
			return 0; 
	} 
	
	// 取中间位置
	// 前述中心分割线位于mid, mid+1之间
	int mid = (left + right) / 2;//求中间位置

	//递归求左边的最大连续子序列之和
	maxLeftSum = MaxSubSum(a, left, mid);	
	//递归求右边的最大连续子序列之和
	maxRightSum = MaxSubSum(a, mid + 1, right);	
	
	// maxLeftBorderSum + maxRightBorderSum表示至少到分割线的最大子序列和
	// 可能求出的是a[someposition...mid]
	// 可能求出的是a[mid+1...someposition]
	// 也可能求出的是a[leftsomeposition...rightsomeposition]
	// 且leftsomeposition<=mid, rightsomeposition>=mid+1
	maxLeftBorderSum = 0, leftBorderSum = 0;
	for(i = mid; i >= left; i --)//求出左边包含a[mid]的子序列的最大和
	{	
		leftBorderSum += a[i];
		if(leftBorderSum > maxLeftBorderSum)
			maxLeftBorderSum = leftBorderSum;
	}
	
	maxRightBorderSum = 0, rightBorderSum = 0;
	for(j = mid + 1; j <= right; j ++)	//求出右边包含a[mid+1]的子序列的最大和
	{	
		rightBorderSum += a[j];
		if(rightBorderSum > maxRightBorderSum)
			maxRightBorderSum = rightBorderSum;
	}
	return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum); 
}

int main()
{	
	int a[] = {-2, 11, -4, 13, -5, -2}, n = sizeof(a) / sizeof(a[0]);
	int b[] = {-6, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2}, m = sizeof(b) / sizeof(b[0]);
	
	printf("a序列的最大连续子序列的和:%ld\n", MaxSubSum(a, 0, n - 1));
	printf("b序列的最大连续子序列的和:%ld\n", MaxSubSum(b, 0, m - 1));

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

岁月辰星

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值