回归经典系列之递归和分治思想下

http://blog.csdn.net/sgnyyy/article/details/7490595

1       分治思想

(Divide-and-Conquer)

分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。

2       分治法解题的一般步骤

(1)分解( Divide  ):,将要解决的问题划分成若干规模较小的同类问题;

(2)求解( Conquer):,当子问题划分得足够小时,用较简单的方法解决;

(3)合并(Combine):,按原问题的要求,将子问题的解逐层合并构成原问题的解。

通用框架:用递归的方式划分到一定程度

分解 直接或递归求解子问题 组合

在递归求解子问题时同样要注意理清递归关系、递归出口、参数设置等问题。

divide_and_conquer(data)

{
            if(data数据量较小)直接处理data得到结果,或者有个结束的判断,因为这个是在递归;
            else
            {
                       A.将data分为多个部分data1,data2,.....datan
                       并分别递归调用divide_and_conquer处理各个部分!
                       B.合并data1,data2,....datan处理之后的结果!
             }
}


 

3       实例1---归并排序

思想:分而治之

每个递归过程涉及三个步骤:

1、 分解:把待排序的n个元素的序列分解成两个子序列,每个子序列包括n/2个元素。

2、 治理:把每个子序列分别调用归并排序MergeSort,进行递归操作。

3、 合并:合并两个排好序的子序列,生成排序结果

归并排序的工作原理:

1、 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列。

2、 设定两个指针,最初位置分别为两个已经排序序列的起始位置

3、 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一个位置

4、 重复步骤3直到某一指针达到序列尾

5、 将另一序列剩下的所有元素直接复制到合并序列尾

代码:

//分治还有个明显的特点是left和right的下标,也就是说有一个分的标志
void Merge(int* array, int* tempararay, int left, int middle, int right)
{
	//左指针尾
	int leftend = middle-1;
	//右指针头
	int rightstart = middle;
	int tempIndex = left;
	//数组合并后的length长度
	int tempLength = right - left + 1;
	//相当于排序,把临时的结果放入到tempararay中
	while( (left <= leftend) && (rightstart <= right) )
	{
		if(array[left]<array[rightstart])
		{
			tempararay[tempIndex++] = array[left++];
		}
		else
		{
			tempararay[tempIndex++] = array[rightstart++];
		}
	}	
	//判断左序列是否结束
	while(left <= leftend)
		tempararay[tempIndex++] = array[left++];

	while(rightstart <= right)
		tempararay[tempIndex++] = array[rightstart++];
	
	//交换临时排序的数据
	for(int i=0; i<tempLength; i++)
	{
		array[right] = tempararay[right];
		cout<<array[right]<<" ";
		right--;
	}
	if(tempLength)
		cout<<endl;
}
void MergeSort(int* array, int* temparray, int left, int right)
{
	if(left < right)
	{
		//1、分解划分成前后两部分
		int middle = (left+right)/2;
		//2、对分解治理
		MergeSort(array, temparray, left, middle);
		//2、对分解治理
		MergeSort(array, temparray, middle+1, right);
		//3、合并
		Merge(array, temparray, left, middle+1, right);
	}
}
int main()
{
	int a[] = {13,7,8,3,60,50,4};
	int len = sizeof(a)/sizeof(int);
	//需要申请一块临时的内存保存排序结果
	int* b = new int[len]; 
	MergeSort(a,b,0,len-1);
	for(int i=0; i<len; i++)
	{
		cout<<a[i]<<",";
	}
}


 

4       快速排序

分而治之

//一趟排序,每一次一趟之后,前半部分是小的,后半部分是大的
int Partition(int *array, int low, int high)
{
	//*array = *(array+low);
	int pivotKey = *(array+low);

	while(low<high)
	{
		while(low<high && *(array+high)>pivotKey)
			--high;
		*(array+low) = *(array+high);
		while(low<high && *(array+low)<pivotKey)
			++low;
		*(array+high) = *(array+low);
	}
	*(array+low) = pivotKey;

	return low;
}
//再写分治法的代码:
void QSort(int *array, int low, int high)
{
	if(low<high)
	{
		//1. 分解
		int middle = Partition(array, low, high);
		//2. 递归求解
		QSort(array, low , middle-1);
		QSort(array, middle+1, high);
		//3. 合并:由于分解出的两个字序列是就地进行的,所以不需要再次合并
	}
}
void BinarySort(int *array, int length, int data)
{
	QSort(array,0,length-1);
}


 

5       循环赛日程

n个选手的比赛日程可以通过n/2个选手的比赛日程来确定,递归调用,直到剩下两个选手的时候,比赛日程就可以很容易的确定

先计算出一部分,然后另一部分赋值

void gametable(int k)
{
	int a[100][100];
	int n,temp,i,j,p,t;
	n=2;
	a[1][1]=1;a[1][2]=2;
	a[2][1]=2;a[2][2]=1;
	for(t=1;t<k;t++)
	{
		temp=n;n=n*2;
		for(i=temp+1;i<=n;i++)
			for(j=1;j<=temp;j++)
				a[i][j]=a[i-temp][j]+temp;
		for(i=1;i<=temp;i++)
		{
			for(j=temp+1;j<=n;j++)
			{
				a[i][j]=a[i+temp][(j+temp)%n];
			}
		}
		for(i=temp+1;i<=n;i++)//将左上角元素抄到右下角
			for(j=temp+1;j<=n;j++)
				a[i][j]=a[i-temp][j-temp];
	}
	cout<<n<<endl;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		{
			cout<<a[i][j]<<" ";
		}
		cout<<endl;
	}
}


6        二分搜索法

int BinarySearch(int a[], int &x, int left, int right)
{
	while(left < right)
	{
		int middle = (left + right)/2;
		if(a[middle] == x) return middle;
		if(a[middle] < x) right = middle - 1;
		else left = middle + 1;
	}
}


 

7   最近点对问题

1)蛮力法:
计算每两个之间的距离,然后比较,也就是计算(n-1)与n个元素之间的比较
2)分治法:
     a.  找中线把n个元素分成左右两部分分别求得两边的最短距离,然后取两者中的最小者,计为len
     b.  在中线两边分别取len的距离。记录该距离范围内点的个数,中线左边有L个元素,右边有R个元素。
     c.  求左边元素到右边元素的距离看其是否小于之前记录的len,小则记录下来。
     d.  此时的右边元素只取y值和左边元素y值距离小于1的(减少循环次数)。
     e.  循环结束即可找到最小的距离

以下是思想 与 程序框架

a[]坐标已有序
float test(CPoint a[],int begin,int end,int len)
{
    if(len>2)
    {
        mid=(end-begin)>>1;
        int d1=test(a,begin,mid);
        int d2=test(a,mid,end);
        int d=d1<d2?d1:d2;
        CPoint* b=cal(a,d);
        float temp=cal_T(b,d);
        return temp<d?temp:d;
    }
    else//len==2
    {//仅仅有两个点 PointA PointB
        return Distance(a,begin,end);
    }
}

8   Strassen矩阵乘法

9 大整数乘法

10:使用分治法求解数组的第k大数

        引申问题:在数组A【】中,找一个元素a[i],使得小于a[i]的放到其左边,大于的放在其右边。

void test_1(int *a,int index,int len)
{
	if(index>=len)
		return;
	swap(a[index],a[len-1]);
	int j=-1;
	int i=0;
	for(int idx=0;idx<len;idx++)
	{
		if(a[idx]<a[len-1])
		{
			swap(a[idx],a[++j]);
		}
	}
	swap(a[++j],a[len-1]);
}
void print(int a[],int len)
{
	for(int i=0;i<len;i++)
		printf("%d",a[i]);
	printf("\n");
}
int a[]={6,2,3,1,0,5};
int a1[]={6,2,3,1,0,5};

void test_2(int *a,int index,int len)
{
	int key=a[index];
	swap(a[index],a[0]);
	print(a,len);
	int j=len-1;
	int i=0;
	while(i<j)
	{
		while(i<j&&a[j]>=key)
		{
			j--;
		}
		if(i<j)
			a[i++]=a[j];
		while(i<j&&a[i]<key)
		{
			i++;
		}
		if(i<j)
			a[j--]=a[i];
	}
	a[i]=key;
}

以下是查找第k个大的数字:

void print(int a[],int len)
{
	for(int i=0;i<len;i++)
		printf("%d",a[i]);
	printf("\n");
}
int a[]={6,2,3,1,0,5};
int a1[]={6,2,3,1,0,5};

int test_2(int *a,int index,int len,int begin)
{
	int key=a[index+begin];
	swap(a[index+begin],a[begin]);
	//print(a,len);
	int j=begin+len-1;
	int i=begin;
	while(i<j)
	{
		while(i<j&&a[j]>=key)
		{
			j--;
		}
		if(i<j)
			a[i++]=a[j];
		while(i<j&&a[i]<key)
		{
			i++;
		}
		if(i<j)
			a[j--]=a[i];
	}
	a[i]=key;
	return i;
}
#include <time.h>
int FindMaxK(int *a,int begin,int end,int k)
{
	int randomPos=rand()%(end-begin+1);
	int len=end-begin+1;
	int temp=test_2(a,randomPos,end-begin+1,begin);
	print(a,6);
	if(k==len-temp+begin)
	{
		return a[temp];
	}
	else if(k<len-temp)
	{
		FindMaxK(a,temp+1,end,k);
	}
	else
		FindMaxK(a,begin,temp-1,k-(len-temp));
}



11 全排列问题与dfs解决组合问题

bool isOK(char* str,int idx,int i)
{
	for(int j=idx;j<i;j++)
	{
		if(str[j]==str[i])
			return false;
	}
	return true;
}
void perm(int idx,int num,int cur,char* str)
{
	if(cur==num)
	{
		for(int i=0;i<num;i++)
			printf("%c",str[i]);
		printf("\n");
	}
	else
	{
		for(int i=idx;i<num;i++)
		{
			if(isOK(str,idx,i))
			{
				swap(str[idx],str[i]);
				perm(idx+1,num,cur+1,str);
				swap(str[idx],str[i]);
			}
		}
	}
}
char str[]={'1','2','2'};
//perm(0,sizeof(str),0,str);

void zuhe(int cur,int num,std::vector<int> col,int idx)
{
	if(cur==4)
	{
		std::vector<int> temp;
		temp=col;
		std::vector<int>::iterator iter=col.begin();
		for(;iter!=col.end();iter++)
			printf("%d",*iter);
		printf("\n");
		return;
	}
	else
	{
		for(int i=idx;i<num;i++)
		{
			col.push_back(i);
			zuhe(cur+1,num,col,i+1);
			col.pop_back();
		}
	}
}


int _tmain(int argc, _TCHAR* argv[])
{
	std::vector<int> col;
	zuhe(0,9,col,0);
	return 0;
}


12 递归解决八皇后问题

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值