数据结构第一个卡住的难点出现了——递归?
四题作业写的很艰难…参考了不少网上的代码
打算利用这几天读一下清华的教材,刷一点题…
毕竟,递归还是特别特别特别特别重要的!!
递归求组合
设计递归算法,从1、2、3…m中任取k个数的所有组合
#include<iostream>
using namespace std;
int x[100];//记录已选数
void search(int i,int rec,int m,int k)
{ int j=0;
if(rec==k){//已经选到了k个数,输出
for(j=0;j<k;j++) cout<<x[j]<<'\t';cout<<endl;return;
}
if(i>m-1) return;//超过范围
x[rec]=i+1;search(i+1,rec+1,m,k);//选取当前的数
search(i+1,rec,m,k);//不选当前的数
}
int main()
{
int m=0,k=0,i=0;
cout<<"输入自然数总数m:"<<endl;
cin>>m;
cout<<"输入k:"<<endl;
cin>>k;
cout<<"从"<<m<<"个自然数中任取"<<k<<"个数的所有组合:"<<endl;
for(i=0;i<m;i++)
{
x[i]=i+1;
}
search(0,0,m,k);
}
不得不承认,递归算法是真的简洁
本来的思路:从m-1个中取k个/ 从m-1个中取k-1个再加m
用了直接输出,事实证明是不能的
这个代码的思路:从前往后递归是否选取当前数字,用数组记录已经选取的数字,用rec记录已经选取的个数
这个思路很有借鉴意义,要从中学习递归的写法(多种情况的并行,怎样调用下一步等)
Ackerman函数
分别写出递归和利用栈的非递归算法
递归的很好写啦,就直接抄进去
#include<iostream>
using namespace std;
int akm(int m,int n)
{
if(!m) return n+1;
else if(!n) return akm(m-1,1);
else return akm(m-1,akm(m,n-1));
}
int main()
{ int n=0,m=0;
cout<<"输入m:"<<endl;
cin>>m;
cout<<"输入n:"<<endl;
cin>>n;
cout<<"akm("<<m<<","<<n<<")=";
cout<<akm(m,n)<<endl;
}
非递归就很有趣了?
#include<iostream>
#include<stack>
using namespace std;
int ackerman(int m, int n)
{
stack<int> s1;
stack<int> s2;
s1.push(m);s2.push(n);
while (!s1.empty())
{
while (m)//m!=0
{
if (!n)//m!=0,n==0,akm(m,n)=akm(m-1,1)
{
m--;n=1;
s1.push(m);
s2.push(n);
}
else//m!=0,n!=0,akm(m,n)=akm(m-1,akm(m,n-1))
{
n--;
s1.push(m - 1);
s2.push(-1);//sign
}
}
n = n + 1;//m==0,akm(m,n)=n+1
while((!s1.empty())&&(s2.top()!=-1))
{
s1.pop();
s2.pop();
}
if(!s1.empty())
{
m = s1.top();
s2.pop();
s2.push(n);
}
}
return n;
}
int main()
{ int n=0,m=0;
cout<<"输入m:"<<endl;
cin>>m;
cout<<"输入n:"<<endl;
cin>>n;
cout<<"akm("<<m<<","<<n<<")=";
cout<<ackerman(m,n)<<endl;
}
思路:两个栈,一个放m,一个放n,m、n都同步变换
入栈按照函数的定义来,只是当出现一个参数为递归的akm时放入-1,以便于后续取出
取出比较复杂:
m=0,n+1
n!=-1,直接pop(因为n已经同步)
n==-1,若m栈非空,m取栈顶,n入栈
其实这个-1模拟了递归回去的过程,等于再次调用了函数
思路很有借鉴意义,递归函数可以利用
判断广义表相等的函数
写一个判断两个广义表相等的函数,第一次调用时s t是广义表头结点
#include<iostream>
using namespace std;
template <class Type> class GenList;
template <class Type> class GenListNode {
friend class Genlist;
private:
int utype; //=0/1/2
GenListNode <Type> *tlink; //指向同级的下一个结点
union { //联合
int ref; Type value; //utype=0,在ref计数,utype=1,在value中储存值
GenListNode <Type> *hlink; //utype=2储存子表头节点
} info;
public:
GenListNode(){info.utype(0), tlink(NULL), info.ref(0);}
GenListNode(GenListNode<Type> *p)
{utype=p->utype; tlink=p->tlink; info=p->info;}
};
template <class Type> class GenList {
private:
GenListNode <Type> *first;
bool Equal(GenListNode<Type> *s,GenListNode<Type> *t);};
template<class Type>
bool GenList<Type>::Equal(GenListNode<Type> *s,GenListNode<Type> *t)
{
if(s!=NULL&&t!=NULL)
{
if(s->info.ref==t->info.ref)
{ s=s->tlink;t=t->tlink;
while(s!=NULL&&t!=NULL)//依次比较
{if((s->utype==1&&t->utype==1&&s->info.value==t->info.value)||(s->utype==2&&t->type==2&&Equal(s->info.hlink,t->info.hlink)))//若储存子表,递归比较
{s=s->tlink;t=t->tlink;}//当前节点相等则后移
else return false;}
if(s==NULL&&t==NULL)return true;//完成全循环则相等
}
else return false;
}
else if(s==NULL&&t==NULL) return true;//都为空表,相等
else return false;//一个空一个不空,不相等
}
这个看看PPT里广义表的类定义还是很有思路的
主要是掌握广义表内储存数据的方式,想到检测子表的递归调用
N皇后问题
经典的N皇后问题,用递归找出所有解法
#include<iostream>
#include<math.h>
using namespace std;
int x[100];
bool check(int k,int j)
{ int i=0;
for(i=0;i<k;i++)
if(x[i]==j||abs(x[i]-j)==abs(k-i)) return false;
if(k==i)return true;
else return false;
}
void setQueen(int k,int n)
{ int i=0;
if(k==n)
{for (i=0;i<n;i++)
cout<<x[i]+1<<'\t';
cout<<endl;return;}
for(i=0;i<n;i++)
{
if(check(k,i)) {x[k]=i;setQueen(k+1,n);}
}
}
int main()
{ int n,i=0;
cout<<"输入n:"<<endl;
cin>>n;
cout<<"n皇后的解法:"<<endl;
for(i=0;i<n;i++)
cout<<"第"<<i+1<<"行"<<'\t';
cout<<endl;
setQueen(0,n);
}
思路:x数组存放各行内有皇后的列
check来检测k行当前列的摆放是否与k-1行都不冲突
setqueen放置皇后,当k到了最后一行,输出
否则,在k行依次按列检测是否合法,合法则跳转到下一行
重要,熟悉思路
- 清华数据结构 递归练习
- 总结递归写法和思路