数据结构与算法(4)分治法

分治者,分而治之也。

分治法(divide and conquer method)的设计思想是将一个难以直接解决的大问题,划分成一些规模较小的子问题,以便各个击破,分而治之。

由分治法产生的子问题往往是原问题的较小模式,反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到能够直接求解,这自然导致递归。

分治与递归经常同时应用在算法设计之中,并由此产生许多高效的算法。


一般来说,分治法的求解过程由以下三个阶段组成:
(1)划分:把规模为n的原问题划分为k个规模较小的子问题。
(2)求解:各子问题的解法与原问题的解法通常是相同的,可以用递归的方法求解各个子问题,有时递归处理也可以用循环来实现。
(3)合并:把各个子问题的解合并起来,合并的代价因情况不同有很大差异,分治算法的有效性很大程度上依赖于合并的实现。


人们从大量实践中发现,在用分治法设计算法时,最好使子问题的规模大致相同。也就是将一个问题划分成大小相等的k个子问题(通常k=2),这种使子问题规模大致相等的做法是出自一种平衡(bal-ancing)子问题的启发式思想。


算法设计实例——数字旋转方阵

【问题】 输出如图所示N×N(1≤N≤10)的数字旋转方阵。
在这里插入图片描述

【想法】 用二维数组data[N][N]表示N×N的方阵,观察方阵中数字的规律,可以从外层向里层填数。

在填数过程中,每一层的起始位置很重要。
设变量size表示方阵的大小,则初始时size=N,填完一层则size=size-2;设变量begin表示每一层的起始位置,变量i和j分别表示行号和列号,则每一层初始时i=begin,j=begin。将每一层的填数过程分为A、B、C、D四个区域,每个区域需要填写size-1个数字,且填写区域A时列号不变行号加1,填写区域B时行号不变列号加1,填写区域C时列号不变行号减1,填写区域D时行号不变列号减1。显然,递归的边界条件是size等于0或size等于1。

【算法】 设递归函数Full实现填数过程,算法用伪代码描述如下 :

算法:Full(number,begin,size)
输入:当前层左上角要填的数字number,左上角的坐标begin,方阵的阶数size
输出:数字旋转方阵

1.如果size等于0,则算法结束;
2.如果size等于1,则data[begin][begin]=number,算法结束;
3.初始化行、列下标i=begin,j=begin;
4.重复下述操作size-1次,填写区域A:
	4.1 data[i][j]=number;number++4.2 行下标i++;列下标不变;
5.重复下述操作size-1次,填写区域B:
	5.1 data[i][j]=number;number++5.2 行下标不变;列下标j++6.重复下述操作size-1次,填写区域C:
	6.1 data[i][j]=number;number++6.2 行下标i--;列下标不变;
7.重复下述操作size-1次,填写区域D:
	7.1 data[i][j]=number;number++7.2 行下标不变,列下标j--8.调用函数Full在size-2阶方阵中左上角begin+1处从数字number开始填数;

实现如下:

扫描二维码关注公众号,回复: 10722625 查看本文章
#include <stdio.h>

#define N 10
int data[N][N] = {0};

void Full(int number, int  begin , int size);
void OutPrint(int size);

int main()
{
	int n;
	printf("请输入方阵的阶数(小于10): ");
	scanf("%d", &n);
	Full(1, 0, n);
	OutPrint(n);
	return 0;
}
void Full(int number, int begin, int size)
{
	int i, j ,k;
	if(size == 0)
		return;
	if(size == 1)	//递归的边界
	{
		data[begin][begin] = number;
		return;
	}

	i = begin, j = begin;
	// 填写区域 A
	for( k = 0;  k<size-1; k++ ){
		data[i][j] = number;
		number ++;
		i ++;
	}
	// 填写区域 B
	for(k =0; k<size -1; k++){
		data[i][j] = number;
		number ++;
		j++;
	}
	// 填写区域 C
	for(k = 0; k<size-1; k++){
		data[i][j]= number; 
		number ++;
		i--;
	}
	// 填写区域 D
	for(k = 0; k<size-1; k++){
		data[i][j]= number; 
		number ++;
		j--;
	}
	Full(number, begin+1, size-2);   // 递归调用
}

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

实测结果如下:

在这里插入图片描述

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

猜你喜欢

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