DS:递归和广义表

数据结构第一个卡住的难点出现了——递归?
四题作业写的很艰难…参考了不少网上的代码
打算利用这几天读一下清华的教材,刷一点题…
毕竟,递归还是特别特别特别特别重要的!!

递归求组合

设计递归算法,从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行依次按列检测是否合法,合法则跳转到下一行
重要,熟悉思路

  • 清华数据结构 递归练习
  • 总结递归写法和思路
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值