分治(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**)一定要注意哦!!!