递归和分治

分治(divide and conquer)是基于多分枝递归的一种算法。简单的说就是把一个大问题分解为多个类型相同的子问题,最后把这些子问题的解合并起来就是问题的解。

我们看一下典型的递归和分治算法。


问题1: 插入排序的递归算法

思路:

1.首先找到突破点->> 如果共有n个数,如果前面n-1个都已排序,那么我只要把最后一个数插入到正确的位置即可。那如何让前n-1个都已排序呢,如果前n-2个已排序就好了...........,一直到第一个已排序,明显的第一个肯定是已排序的,那么这就是停止条件。

2.当有了思路后,写递归函数时一定要确信自己的递归函数没错,不要想如果错了怎么办?,如sort(a,n-1),就是代表前n-1个数都已排序。

3.当合并数据时,直接考虑最后合并的情况。如当前n-1个数都是已排序的,那么就考虑如何将最后一个数插入到合适位置(如下merge函数)

#include <iostream>
using namespace std;
void merge(int a[],int );

void sort(int a[],int n) // n the num of a
{
	if(n!=1)
	{
		sort(a,n-1);
		merge(a,n);
	}
}

void merge(int a[],int j)
{
	int i;
	int k=a[j-1];
	for(i=j-2;i>-1;--i)
	{
		if(k<a[i])
			a[i+1]=a[i];
		else
			break;
	}
	a[i+1]=k;
}

void show(int *beg,int *end)
{
	while(beg!=end)
	{
		cout<<*beg++<<" ";
	}
}

int main()
{
int a[]={3,2,1,4,8,5,0,7,6};
sort(a,9);
show(a,a+9);

cout<<endl;
}

输出:

1 2 3 4 5 6 7 8

问题2:二分查找法递归版

这个问题很简单吧。。。。,前提是序列已排序。

#include <iostream>
using namespace std;

int binary_find_recur(int a[],int key,int start,int end)
{
	int middle=(start+end)/2;
	if(a[middle]==key)
		return middle;
	if(start==middle)
		return -1;
        if(key<a[middle])
		binary_find_recur(a,key,start,middle);
	else
		binary_find_recur(a,key,middle,end);
		
}


int main()
{
int a[]={1,2,3,4,5,6,7,8,9,10};
cout<<binary_find_recur(a,10,0,10);

cout<<endl;
}

输出:

9

这里提供一下库函数bsearch的实现:

#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;

void *binary_find_recur(const void *base,const void *key,int start,int end,size_t size,int (*compar)(const void *, const void *)) // return the index 
{
	int middle=(start+end)/2;
	void *middle_add=(char*)base+middle*size;
	if(compar(middle_add,key)==0)
		return middle_add;
	if(start==middle)
		return NULL;
	if(compar(key,middle_add)<0)
		binary_find_recur(base,key,start,middle,size,compar);
	else
		binary_find_recur(base,key,middle,end,size,compar);
		
}

void *b_search(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
{
	return binary_find_recur(base,key,0,nmemb,size,compar);
}

int comInt(const void *f,const void *s)
{
	return *(int*)f-*(int*)s;
}

int comChar(const void *s1, const void *s2)
{
	return strcmp(*(char**)s1,*(char**)s2);
}

int main()
{
int a[]={1,2,3,4,5,6,7,8,9,10};
int key=1;
if(b_search(&key,a,10,sizeof(int),comInt)!=NULL)
	cout<<"find....\n";
char *s[]={"baobao","lwy","yihaibo","yiluo"};
char *ckey="yihaibo";
if(b_search(&ckey,s,4,sizeof(char*),comChar)!=NULL)
	cout<<"find....";

cout<<endl;
}



问题3:求x的n次方

平时我们用for循环求x的n次方,那样算法时间复杂度为n,下面的递归版本时间复杂度为logn

#include <iostream>
using namespace std;

long power_recur(int base,int n)
{
	if(n==1)
		return base;
	if(n%2==0)
	{
		int tmp=power_recur(base,n/2);
		return tmp*tmp;
	}
	else
	{
		int tmp=power_recur(base,(n-1)/2);
		return tmp*tmp*base;
	}
}

int main()
{
cout<<power_recur(2,11);
cout<<endl;
}

输出:

2048

问题4:斐波拉契数列递归版

下面也许你真的没看见过,呵呵

这个递归版本的时间复杂度是logn,呵呵,以前写的版本运行时间可是指数级别的。。。。

主要是基于以下原理:

于是自己写个小Matrix类,2维矩阵

#include <iostream>
using namespace std;

class Matrix
{
private:
	int m00,m01,m10,m11;
public:
	Matrix(int a=1,int b=1,int c=1,int d=0):m00(a),m01(b),m10(c),m11(d){ }
	friend Matrix operator*(const Matrix& first,const Matrix& second);
	friend ostream& operator<<(ostream&,const Matrix&);
};

Matrix operator*(const Matrix& first,const Matrix& second)
{
	Matrix ret;
	ret.m00=first.m00*second.m00+first.m01*second.m10;
	ret.m01=first.m00*second.m01+first.m01*second.m11;
	ret.m10=first.m10*second.m00+first.m11*second.m10;
	ret.m11=first.m10*second.m01+first.m11*second.m11;
	return ret;
}

inline ostream& operator<<(ostream& out,const Matrix& ret)
{
	out<<ret.m01;
	return out;
}

Matrix fib_matrix(int n)
{
	if(n==1)
		return Matrix();
	if(n%2==0)
	{
		return fib_matrix(n/2)*fib_matrix(n/2);
	}
	else
	{
		return fib_matrix((n-1)/2)*fib_matrix((n-1)/2)*Matrix();
	}
}

int main()
{
cout<<fib_matrix(7);
cout<<endl;
}

输出:

13

问题5:归并排序

这个可以说是递归和分治最典型例子了,思想就不说了,看代码

#include <iostream>
using namespace std;

void merge(int a[],int p,int q,int r)
{
int i,j,k;
int n1=q-p+1;
int n2=r-q;
int L[n1],R[n2];
for(i=0;i<n1;++i)
	L[i]=a[p+i];
for(j=0;j<n2;++j)
	R[j]=a[q+j+1];
i=0,j=0,k=p;

while((i<n1)&&(j<n2))
{
	if(L[i]<=R[j])
		a[k]=L[i++];
	else
		a[k]=R[j++];
	++k;
}

while(i<n1)
	a[k++]=L[i++];
while(j<n2)
	a[k++]=R[j++];

}


void merge_sort(int a[],int p,int r)
{
	if(p<r)
	{
		int q=(p+r)/2;
		merge_sort(a,p,q);
		merge_sort(a,q+1,r);
		merge(a,p,q,r);
	}
}

void show(int a[])
{
	for(int i=0;i<10;i++)
		cout<<a[i]<<" ";
}


int main()
{
int a[]={1,0,9,3,2,4,5,7,6,8};
merge_sort(a,0,9);
show(a);

cout<<endl;
}
输出:0 1 2 3 4 5 6 7 8 9

问题6:快速排序

#include <iostream>
using namespace std;

void exchange(int& a,int& b)
{
	int tmp;
	tmp=a;
	a=b;
	b=tmp;
}
int partition(int a[],int beg,int end)
{
	int x=a[end];
	int i=beg-1;
	for(int j=beg;j<end;++j)
	{
		if(a[j]<=x)
		{
			++i;
			exchange(a[j],a[i]);
		}
	}
	exchange(a[i+1],a[end]);
	return i+1;
}

void quick_sort(int a[],int beg,int end)
{
	if(beg<end)
	{
		int middle=partition(a,beg,end);
		quick_sort(a,beg,middle-1);
		quick_sort(a,middle+1,end);
	}
}

int main()
{
	int a[10]={1,4,5,2,9,6,8,0,3,7};
	quick_sort(a,0,9);
	int *p=a;
	while(p!=(a+10))
		cout<<*p++<<" ";

	cout<<endl;
}

快速排序的最差时间复杂度是n的平方(对应于输入序列已排序或已逆序),平均时间复杂度是nlogn,不过如果是随机版本的话复杂度还是nlogn.

快速排序还是很快的,一般比归并排序快3倍左右。

下面是我自己实现的C版本的qsort函数,呵呵,而且是随机版本,主要是刚好在学泛型编程,所以就自己实现了一下。

#include <iostream>
#include <time.h>
#include <cstdlib>
#include <cstring>
using namespace std;

void exchange(void *s,void *d,int size)
{
	void *tmp=malloc(size);
	memcpy(tmp,s,size);
	memcpy(s,d,size);
	memcpy(d,tmp,size);
}

int partition(void *base,int beg,int end,int size,int (*compar)(const void *,const void *))
{
	srandom((unsigned)time(0));
	int randomnu=random()%(end-beg)+beg;
	void *x=(char*)base+end*size;
	exchange(x,(char*)base+randomnu*size,size);
	int i=beg-1;
	for(int j=beg;j<end;++j)
	{
		if(compar(x,(char*)base+j*size)>=0)
		{
			++i;
			exchange((char*)base+j*size,(char*)base+i*size,size);
		}
	}
	exchange((char*)base+(i+1)*size,(char*)base+end*size,size);
	return i+1;
}

void quick_sort(void *base,int beg,int end,int size,int (*compar)(const void *,const void *))

{
	if(beg<end)
	{
		int middle=partition(base,beg,end,size,compar);
		quick_sort(base,beg,middle-1,size,compar);
		quick_sort(base,middle+1,end,size,compar);
	}
}


void q_sort(void *base,size_t nmemb,size_t size,int (*compar)(const void *,const void *))
{
	quick_sort(base,0,nmemb-1,size,compar);
}

int comparInt(const void *s1,const void *s2)
{
	return *(int*)s1-*(int*)s2;
	
}

int comparChar(const void *s1, const void *s2)
{
	return strcmp(*(char**)s1,*(char**)s2);
}

int main()
{
int a[10]={2,8,7,1,3,5,6,4};
q_sort(a,8,sizeof(int),comparInt);
int *q=a;
while(q!=(a+8))
	cout<<*q++<<" ";

cout<<endl;
char *s[]={"yhb","lwy","yiluo","baobao"};
q_sort(s,4,sizeof(char*),comparChar);
char **p=s;
while(p!=(s+4))
	cout<<*p++<<" ";

cout<<endl;
}
输出:
1 2 3 4 5 6 7 8 
baobao lwy yhb yiluo 

我一直觉得c字符串挺复杂的。。。

亲,return strcmp(*(char**)s1,*(char**)s2); 这个是(char**)一定要注意哦!!!


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值