分治与递归(算法分析与设计)

1.分治法基本模板

divide-and-comquer(P)
{
	if(|P|<=n0)
		adhoc(P);
	divide P into smaller subinstances P1,P2,...,Pk;
	for(i=1;i<=k;i++)
		yi=divide-and-comquer(Pi);
	return merge(y1,...,yk);
}

a.一般是递归出口

b.将原问题分割成若干子问题,子问题与原问题相同,规模略小

c.合并子问题的解(不是所有分治都有这个)

2.递归与非递归的转换记录

a.尾递归转非递归(一般先写好递归版,再用栈模拟)(本质:还是自顶而下

尾递归在原问题转换成若干子问题之后,不再合并子问题的解,递归是最后的语句,递归之后不再有操作返回值或局部变量的表达式。

1.循环外压栈初始变量

2.循环以栈空为条件

3.循环内先出栈,恢复局部变量值

4.递归出口处,仍然编写递归出口处代码,由continue代替return来打断余下操作

5.用恢复的局部变量值操作

6.遇到递归处,改成压栈当前局部变量

7.出循环

b.线性(一般)递归转非递归

若用栈模拟递归,保存当前局部变量用以代替递归,但是递归之后,又需要用这些改变后的局部变量(或递归返回值)做子问题的合并,这个对我来说比较难做。所以换个方向,思考记录如下:
1.尾递归转非递归的本质还是自顶而下的处理问题,所以可以用栈轻易模拟

2.非尾递归可以自底而上,来转化递归

尾递归转非递归: 3.棋盘覆盖,4.快速排序

非尾递归转非递归:5.strassen矩阵乘法,6.合并排序

3.棋盘覆盖

a.递归版

#include<iostream>
#include<vector> 
using namespace std;
vector< vector<int> > a;
static int tile=1;
void chessBoard(int startx,int starty,int msize,int x,int y)
{
	if(msize==1)
	{
		return;
	}
	else
	{
		int g=tile++;//只能用局部变量来赋值,直接用静态变量,递归返回后,余下伪特殊方块的赋值就出问题了。 
		int tempx=(x-startx)/(msize/2);
		int tempy=(y-starty)/(msize/2);
		if(tempx==0&&tempy==0)
		{
			chessBoard(startx,starty,msize/2,x,y);
		}
		else
		{
			a[startx+msize/2-1][starty+msize/2-1]=g;
			chessBoard(startx,starty,msize/2,startx+msize/2-1,starty+msize/2-1);
			
		}
		
		if(tempx==0&&tempy==1)
		{
			chessBoard(startx,starty+msize/2,msize/2,x,y);
		}
		else
		{
			a[startx+msize/2-1][starty+msize/2]=g;
			chessBoard(startx,starty+msize/2,msize/2,startx+msize/2-1,starty+msize/2);
			
		}
		
		if(tempx==1&&tempy==0)
		{
			chessBoard(startx+msize/2,starty,msize/2,x,y);
		}
		else
		{
			a[startx+msize/2][starty+msize/2-1]=g;
			chessBoard(startx+msize/2,starty,msize/2,startx+msize/2,starty+msize/2-1);
			
		}
		
		if(tempx==1&&tempy==1)
		{
			chessBoard(startx+msize/2,starty+msize/2,msize/2,x,y);
		}
		else
		{
			a[startx+msize/2][starty+msize/2]=g;
			chessBoard(startx+msize/2,starty+msize/2,msize/2,startx+msize/2,starty+msize/2);
		}
	}
} 

int main()
{
	//初始化棋盘a,特殊元素赋值0,其余元素赋值-1 
	int n;
	cout<<"please input n!"<<endl;
	cin>>n;
	vector<int> temp;
	for(int j=0;j<n;j++)
	{
		temp.push_back(-1);
	}
	for(int i=0;i<n;i++)
	{
		a.push_back(temp);
	}
	cout<<"please input the special box coordinates!"<<endl;
	int x,y;
	cin>>x>>y;
	a[x][y]=0;
	//特殊元素赋值0,伪特殊元素均大于0,未被标记的均为-1 
	chessBoard(0,0,n,x,y);
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			cout<<a[i][j]<<"\t";
		}
		cout<<endl;
	}
}

b.非递归版

#include<iostream>
#include<vector> 
#include<stack>
using namespace std;
typedef struct Node
{
	public:
		int startx;
		int starty;
		int msize;
		int x;
		int y;
};
vector< vector<int> > a;//存放棋盘 
stack<Node> s;//存放临时变量 

void chessBoard(int startx,int starty,int msize,int x,int y)
{
	Node temp;//临时变量 
	int g=1; 
	temp.startx=startx;
	temp.starty=starty;
	temp.msize=msize;
	temp.x=x;
	temp.y=y;
	s.push(temp);//初始状态压栈 
	
	while(!s.empty())//栈空为循环终止判断条件 
	{
		Node temp2=s.top();//出栈,恢复局部变量 
		s.pop();
		if(temp2.msize==1)//递归出口处,return用continue代替,打断余下操作 
		{
			continue;	
		}
		if((temp2.x-temp2.startx)<temp2.msize/2&&(temp2.y-temp2.starty)<temp2.msize/2)
		{
			temp.startx=temp2.startx;
			temp.starty=temp2.starty;
			temp.msize=temp2.msize/2;
			temp.x=temp2.x;
			temp.y=temp2.y;
			s.push(temp);//先用递归写,写非递归时递归处,用栈保存局部变量,其余各处也一样 
		}
		else
		{
			temp.startx=temp2.startx;
			temp.starty=temp2.starty;
			temp.msize=temp2.msize/2;
			temp.x=temp2.startx+temp2.msize/2-1;
			temp.y=temp2.starty+temp2.msize/2-1;
			s.push(temp);
			if(a[temp2.startx+temp2.msize/2-1][temp2.starty+temp2.msize/2-1]==-1)
				a[temp2.startx+temp2.msize/2-1][temp2.starty+temp2.msize/2-1]=g;
		}
		if((temp2.x-temp2.startx)<temp2.msize/2&&(temp2.y-temp2.starty)>temp2.msize/2)
		{
			temp.startx=temp2.startx;
			temp.starty=temp2.starty+temp2.msize/2;
			temp.msize=temp2.msize/2;
			temp.x=temp2.x;
			temp.y=temp2.y;
			s.push(temp);
		}
		else
		{
			temp.startx=temp2.startx;
			temp.starty=temp2.starty+temp2.msize/2;
			temp.msize=temp2.msize/2;
			temp.x=temp2.startx+temp2.msize/2-1;
			temp.y=temp2.starty+temp2.msize/2;
			s.push(temp);
			if(a[temp2.startx+temp2.msize/2-1][temp2.starty+temp2.msize/2]==-1)
				a[temp2.startx+temp2.msize/2-1][temp2.starty+temp2.msize/2]=g;	
		}
		if((temp2.x-temp2.startx)>temp2.msize/2&&(temp2.y-temp2.starty)<temp2.msize/2)
		{
			temp.startx=temp2.startx+temp2.msize/2;
			temp.starty=temp2.starty;
			temp.msize=temp2.msize/2;
			temp.x=temp2.x;
			temp.y=temp2.y;
			s.push(temp);
		}
		else
		{
			temp.startx=temp2.startx+temp2.msize/2;
			temp.starty=temp2.starty;
			temp.msize=temp2.msize/2;
			temp.x=temp2.startx+temp2.msize/2;
			temp.y=temp2.starty+temp2.msize/2-1;
			s.push(temp);
			if(a[temp2.startx+temp2.msize/2][temp2.starty+temp2.msize/2-1]==-1)
				a[temp2.startx+temp2.msize/2][temp2.starty+temp2.msize/2-1]=g;
		}
		if((temp2.x-temp2.startx)>temp2.msize/2&&(temp2.y-temp2.starty)>temp2.msize/2)
		{
			temp.startx=temp2.startx+temp2.msize/2;
			temp.starty=temp2.starty+temp2.msize/2;
			temp.msize=temp2.msize/2;
			temp.x=temp2.x;
			temp.y=temp2.y;
			s.push(temp);
		}
		else
		{
			temp.startx=temp2.startx+temp2.msize/2;
			temp.starty=temp2.starty+temp2.msize/2;
			temp.msize=temp2.msize/2;
			temp.x=temp2.startx+temp2.msize/2;
			temp.y=temp2.starty+temp2.msize/2;
			s.push(temp);
			if(a[temp2.startx+temp2.msize/2][temp2.starty+temp2.msize/2]==-1)
				a[temp2.startx+temp2.msize/2][temp2.starty+temp2.msize/2]=g;
		}
		g++;
	}
	return;
} 

int main()
{
	int n;
	cout<<"please input n!"<<endl;
	cin>>n;
	vector<int> temp;
	for(int j=0;j<n;j++)
	{
		temp.push_back(-1);
	}
	for(int i=0;i<n;i++)
	{
		a.push_back(temp);
	}
	cout<<"please input the special box coordinates!"<<endl;
	int x,y;
	cin>>x>>y;
	a[x][y]=0;
	chessBoard(0,0,n,x,y);
	
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			cout<<a[i][j]<<"\t";
		}
		cout<<endl;
	}
	return 0;
}

4.快速排序(随机快速排序:随机在left~right之间选择一个主元,再将主元与最左(右)元素互换位置,其他一切照旧)(按各主元对称的序列,快排效果最好)

a.递归版

#include<iostream>
#include<vector>
using namespace std;
int qSortOne(vector<int> &p,int left,int right)
{
	int a=p[left];
	while(left<right)
	{
		while(a<p[right])
		{
			right--;
		}
		p[left]=p[right];
		p[right]=a;
		while(a>p[left])
		{
			left++;
		}
		p[right]=p[left];
		p[left]=a;
	}	
	return left;	
}
void qSort(vector<int> &w,int left,int right)
{
	if(left<right)
	{
		int start=qSortOne(w,left,right);
		qSort(w,left,start-1);
		qSort(w,start+1,right);
	}
}
int main()
{
	vector<int> a;
	int temp;
	while(cin>>temp)
	{
		a.push_back(temp);
	}	
	qSort(a,0,a.size()-1);
	for(int i=0;i<a.size();i++)
	{
		cout<<a[i]<<"\t";
	}
	cout<<endl;
} 

b.非递归版

#include<iostream>
#include<vector>
#include<stack>
using namespace std;
typedef struct Node
{
	int left;
	int right;
};
stack<Node> s;
int qSortOne(vector<int> &p,int left,int right)
{
	int a=p[left];
	while(left<right)
	{
		while(a<p[right])
		{
			right--;
		}
		p[left]=p[right];
		p[right]=a;
		while(a>p[left])
		{
			left++;
		}
		p[right]=p[left];
		p[left]=a;
	}	
	return left;	
}
void qSort(vector<int> &w,int left,int right)
{
	Node temp;
	temp.left=left;
	temp.right=right;
	s.push(temp);
	while(!s.empty())
	{
		Node temp1=s.top();
		s.pop();
		if(temp1.left<temp1.right)
		{
			int start=qSortOne(w,temp1.left,temp1.right);
			temp.left=temp1.left;
			temp.right=start-1;
			s.push(temp);
			temp.left=start+1;
			temp.right=temp1.right;
			s.push(temp);
		}	
	}
}
int main()
{
	vector<int> a;
	int temp;
	while(cin>>temp)
	{
		a.push_back(temp);
	}	
	qSort(a,0,a.size()-1);
	for(int i=0;i<a.size();i++)
	{
		cout<<a[i]<<"\t";
	}
	cout<<endl;
} 

5.strassen矩阵乘法

a.非递归版

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	int n;
	cout<<"please input n!"<<endl;
	cin>>n;
	vector< vector<int> > A;//A作为乘数矩阵A,和存储结果矩阵C 
	vector< vector<int> > B;
	cout<<"please input matrix A!"<<endl;
	for(int i=0;i<n;i++)
	{
		int temp1;
		vector<int> temp2;
		for(int j=0;j<n;j++)
		{
			cin>>temp1;
			temp2.push_back(temp1);
		}
		A.push_back(temp2);
	}
	cout<<"please input matrix B!"<<endl;
	for(int i=0;i<n;i++)
	{
		int temp1;
		vector<int> temp2;
		for(int j=0;j<n;j++)
		{
			cin>>temp1;
			temp2.push_back(temp1);
		}
		B.push_back(temp2);
	}	
	cout<<"count matrix C!"<<endl;
	for(int i=0;i<n;i=i+2)
	{
		vector<int> M;
		for(int j=0;j<n;j=j+2)
		{
			M.push_back(A[i][j]*(B[i][j+1]-B[i+1][j+1]));
			M.push_back((A[i][j]+A[i][j+1])*B[i+1][j+1]);
			M.push_back((A[i+1][j]+A[i+1][j+1])*B[i][j]);
			M.push_back(A[i+1][j+1]*(B[i+1][j]-B[i][j]));
			M.push_back((A[i][j]+A[i+1][j+1])*(B[i][j]+B[i+1][j+1]));
			M.push_back((A[i][j+1]-A[i+1][j+1])*(B[i+1][j]+B[i+1][j+1]));
			M.push_back((A[i][j]-A[i+1][j])*(B[i][j]+B[i][j+1]));
			A[i][j]=M[4]+M[3]-M[1]+M[5];
			A[i][j+1]=M[0]+M[1];
			A[i+1][j]=M[2]+M[3];
			A[i+1][j+1]=M[4]+M[0]-M[2]-M[6];
		}
	} 
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			cout<<A[i][j]<<"\t";
		}
		cout<<endl;
	}
}

6.合并排序

a.递归版

#include<iostream>
#include<vector>
#include<cstdlib>
using namespace std;
int random(int s,int e)
{
	return s+rand()%(e-s+1);
}
void mergeD(vector<int> &a,vector<int> &b,int l,int mid,int r)
{
	int i=l;
	int j=mid+1;
	while(i<=mid&&j<=r)
	{
		if(a[i]<a[j])
		{
			b.push_back(a[i++]);
		}
		else
		{
			b.push_back(a[j++]);
		}
	}
	while(i<=mid)
	{
		b.push_back(a[i++]);		
	}
	while(j<=r)
	{
		b.push_back(a[j++]);
	}
}
void copyD(vector<int> &a,vector<int> &b,int l,int r)
{
	for(int i=l;i<=r;i++)
	{
		a[i]=b[i-l];
	}
}
void mergeSort(vector<int> &d,int l,int r)
{
	if(l<r)
	{
		int i=(r+l)/2;
		mergeSort(d,l,i);	
		mergeSort(d,i+1,r);
		vector<int> b;
		mergeD(d,b,l,i,r);
		copyD(d,b,l,r);
	}
}
int main()
{
	vector<int> data;
	int temp;
	cin>>temp;
	for(int i=0;i<temp;i++)
	{
		data.push_back(random(0,40));
	}
	for(int i=0;i<temp;i++)
	{
		cout<<"data["<<i<<"]: "<<data[i]<<"\t";
	}
	cout<<"\n"<<endl;
	mergeSort(data,0,temp-1);
	for(int i=0;i<temp;i++)
	{
		cout<<"data["<<i<<"]: "<<data[i]<<"\t";
	}
	cout<<endl;
} 

b.非递归版

#include<iostream>
#include<vector>
#include<cstdlib>
#include<stack>
using namespace std;
int random(int s,int e)
{
	return s+rand()%(e-s+1);
}
void mergeD(vector<int> &a,vector<int> &b,int l,int mid,int r)
{
	int i=l;
	int j=mid+1;
	while(i<=mid&&j<=r)
	{
		if(a[i]<a[j])
		{
			b.push_back(a[i++]);
		}
		else
		{
			b.push_back(a[j++]);
		}
	}
	while(i<=mid)
	{
		b.push_back(a[i++]);		
	}
	while(j<=r)
	{
		b.push_back(a[j++]);
	}
}
void copyD(vector<int> &a,vector<int> &b,int l,int r)
{
	for(int i=l;i<=r;i++)
	{
		a[i]=b[i-l];
	}
}
//尾递归转非递归用栈模拟(本质上还是自顶而下)
//线性递归转非递归用栈模拟比较困难,那么就换种思路,自底而上 
void mergeSort(vector<int> &d)
{
	int size=2;//排序小组大小 
	int n=d.size(); 
	int left;
	int right;
	while(size<=n)
	{
		for(left=0;left+size-1<=n-1;left=left+size)
		{
			right=left+size-1;
			vector<int> b;
			mergeD(d,b,left,(right+left)/2,right);
			copyD(d,b,left,right);
		}
		
		if(right<n-1)
		{
			right=n-1;
			vector<int> b;
			mergeD(d,b,left,left+size/2-1,right);
			copyD(d,b,left,right);
		}
		size=size*2;
	}
	size=size/2;
	if(size!=n) 
	{
		left=0;
		right=n-1;
		vector<int> b;
		mergeD(d,b,left,left+size-1,right);
		copyD(d,b,left,right);
	}
}
int main()
{
	vector<int> data;
	int temp;
	cin>>temp;
	for(int i=0;i<temp;i++)
	{
		data.push_back(random(0,40));
	}
	for(int i=0;i<temp;i++)
	{
		cout<<"data["<<i<<"]: "<<data[i]<<"\t";
	}
	cout<<"\n"<<endl;
	mergeSort(data);
	for(int i=0;i<temp;i++)
	{
		cout<<"data["<<i<<"]: "<<data[i]<<"\t";
	}
	cout<<endl;
} 

7.线性时间选择(在序列中找出第k小的数(中位数等等))

a.寻找第k小元素(随机划分线性时间选择)

#include<iostream>
#include<vector>
#include<cstdlib>
using namespace std;
int random(int start,int end)//产生一个(start,end)之间的随机整数 
{
    return start+rand()%(end-start+1);//rand()产生一个随机整数 
}
int qSortOne(vector<int> &p,int left,int right)//随机划分一次(一次快排划分) 
{
	int i=(int)(random(left,right));
	int a=p[left];
	p[left]=p[i];
	p[i]=a;
	a=p[left];
	while(left<right)
	{
		while(a<p[right])
		{
			right--;
		}
		p[left]=p[right];
		p[right]=a;
		while(a>p[left])
		{
			left++;
		}
		p[right]=p[left];
		p[left]=a;
	}	
	return left;	
}
void findK(vector<int> &w,int left,int right,int k)
{
	if(left<=right)
	{
		int start=qSortOne(w,left,right);
		if(start>k)//只递归一边 
		{
			findK(w,left,start-1,k);
		}
		else if(start<k)
		{
			findK(w,start+1,right,k);	
		}
		else
		{
			cout<<"the number data["<<k<<"] is: "<<w[start]<<endl;
		}
	}
}
int main()
{
	vector<int> a;
	int temp;
	int k;
	cout<<"please input k!"<<endl;
	cin>>k;
	cout<<"please input array a[],end by ctrl+z!"<<endl;
	while(cin>>temp)
	{
		a.push_back(temp);
	}	
	findK(a,0,a.size()-1,k);
	for(int i=0;i<a.size();i++)
	{
		cout<<a[i]<<"\t";
	}
}

b.寻找第k小元素(选择划分基准线性时间选择)

#include<iostream>
#include<vector>
#include<cstdlib>
#include<algorithm>
using namespace std;
int random(int start,int end)
{
    return start+rand()%(end-start+1);
}
//比较元素已然确定,就不需要再在函数内部选了 
int Partition(vector<int> &d,int left,int right,int k)
{
	int i = left-1,j = right + 1;
	while(true)
	{
		while(d[++i]<k&&i<right);
		while(d[--j]>k);
		if(i>=j)
		{
			break;
		}
		swap(d[i],d[j]);
	}	
	return j;
}
//冒泡排序,用于对小组内元素排序 
void Bubble(vector<int> &d,int left,int right)
{
	bool exchange;
	for(int i=0;i<=right-left;i++)
	{
		exchange=false;
		for(int j=left;j<right-i;j++)
		{
			if(d[j]>d[j+1])
			{
				swap(d[j],d[j+1]);
				exchange=true;
			}
		}
		if(!exchange)
		{
			return;
		}
	}
}
int Select(vector<int> &d,int left,int right,int k)
{ 
	if(left-right<75)//如果排序个数小于75个,就直接排序,返回第k大小的元素值 
	{
		Bubble(d,left,right);
		return d[left+k-1];
	}
	int i;
	//如果排序个数大于75个,再用找中位数的中位数的方法来减少运算次数
	for(i=0;i<=(right-left-4)/5;i++) 
	{
		Bubble(d,left+i*5,left+i*5+4);//各小组内部排序 
		swap(d[left+i*5+2],d[left+i]);//将各小组的中位数都移到数组最左端 
	} 
	i--;
	if((left+i*5+4)<right)//处理最后一小组,组员不足5个的情况 
	{
		Bubble(d,left+i*5+4+1,right);
		swap(d[(right-(left+i*5+4))/2],d[left+i+1]);
	}
	int dx=Select(d,left,left+(right-left-4)/5,(right-left-4)/(2*5));//找中位数的中位数的值 
	int Nk=Partition(d,left,right,dx);//以中位数的中位数为界,分割数组,返回中位数位置 
	int num=Nk-left+1;//比中位数小的数的数量
	if(num<=k)
	{
		return Select(d,left,Nk,k);//如果k小于等于num,在中位数的中位数的左边找 
	} 
	else
	{
		return Select(d,Nk+1,right,k-num);
	}
}
int main()
{
	vector<int> data;
	int n;
	cout<<"please input the size of array data[]!"<<endl;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		data.push_back(random(0,500)); 
	}
	int k;
	cout<<"please input the k you want!"<<endl;
	cin>>k;
	cout<<"the number is: "<<Select(data,0,n-1,k)<<endl;
	for(int i=0;i<data.size();i++)
	{
		cout<<"data["<<i+1<<"]: "<<data[i]<<"\t";
	}
	cout<<endl;
}

8.最接近点对问题

a.O(n^2)暴力法

#include<iostream>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
typedef struct Node
{
	int x;
	int y;
};
vector<Node> d;
int random(int s,int e)
{
	return s+rand()%(e-s+1);
}
double dis(int a,int b)
{
	return sqrt(pow(d[b].x-d[a].x+0.0,2)+pow(d[b].y-d[a].y+0.0,2));	
}
bool cmpxy(Node a,Node b) 
{
	if(a.x!=b.x)
	{
		return a.x<b.x;
	}
	return a.y<b.y;
}
bool cmpy(int a,int b)
{
	return a<b;
}
double Count(int left,int right)
{
	vector<int> temt;
	if(left+1==right)//只有两个元素,直接返回 
	{
		return dis(right,left);
	}
	else if(left+2==right)//只有三个元素,按中位数分后,有一边只有一个元素,所以也是直接计算,返回结果 
	{
		double d1=dis(right,left);
		double d2=dis(right,left+1);
		double d3=dis(right-1,left);
		return min(min(d1,d2),d3);
	}
	double d1=Count(left,(left+right)/2);//以x坐标排序中位线划分左区 
	double d2=Count((left+right)/2+1,right);//以x坐标排序中位线划分右区 
	double dx=min(d1,d2);
	for(int i=left;i<=right;i++)//没有按《算法设计与分析》中描写一样,这里是直接用暴力,将整个[-dx,dx]区间的数给囊括了 
	{
		if(fabs(d[(left+right)/2].x-d[i].x)<dx)
		{
			temt.push_back(i);
		}
	} 
	sort(temt.begin(),temt.end(),cmpy);//在区间囊括的数,按y值,进行排序 
    for(int i=0;i<temt.size();i++)
    {
        for(int j=i+1;j<temt.size()&&d[temt[j]].y-d[temt[i]].y<dx;j++) 
        {
            double d3=dis(temt[i],temt[j]);//囊括在区间的数,也不分是SL的还是SR的,凡是纵坐标之差小于dx的,全部暴力求解dis 
            if(dx>d3)					   //所以这个算法的复杂度是O(n^2),而不是算法设计与分析》中的n*logn 
            {
            	dx=d3;
            }    
        }
    }
    return dx;
}
int main()
{
	int num;
	cin>>num;
	Node temp;
	for(int i=0;i<num;i++)
	{
		temp.x=random(0,200);
		temp.y=random(0,200);
		d.push_back(temp);
		cout<<temp.x<<"  "<<temp.y<<endl;	
	}	
	sort(d.begin(),d.end(),cmpxy);//没有使用线性时间查找中位数的方法,而是直接排序求各级的中位数了 
	cout<<Count(0,num-1)<<endl;
} 

    原文作者:递归算法
    原文地址: https://blog.csdn.net/chinajane163/article/details/48626515
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞