专题二:递归

daylove 2004-11-02 10:15:40
递归定义:
在定义一个过程或函数的时候,又出现了调用本过程或者函数的成分,即调用它自己本身,这称为直接递归,若过程或函数f调用过程或函数g,而g又调用f,这称为间接递归。
递归模型:
递归模型反映一个递归问题的递归结构,例如:
(1)、f(1)
(2)、f(n)=n*f(n-1) n>1
其中,(1)给出了递归的终止条件,一般我们称为递归出口,(2)给出了f(n)的值与f(n-1)的值之间的关系,称为递归体。
一般地,一个递归模型是由递归出口和递归体构成,前者确定递归到何时为止,后者确定递归的方法。
递归的执行过程:
递归是把一个不能或不好直接求解的“大问题”转化成一个或几个“小问题”来解决。再把这些“小问题”进一步分解成更小的问题来解决。如此分解,直到每个小问题都可以直接解决(此时分解到递归出口)。在这过程中,应该注意:“大问题”和“小问题”必须类似。分解过程中一但遇到递归出口,分解过程结束,开始求值过程。因此,递归的执行过程由分解和求值两部分构成。
求解递归问题有两中方法,一种是直接求值,不需要回溯,另一种是不能直接求值,需要回溯,这两种方式在转换成非递归问题时采用的方法也不同,直接求值使用一些间接变量保存中间结果,称为直接转换法。间接求值需要回溯,所以有用栈保存中间结果,称为间接转换法。

下面举例来进一步说明问。

void reverse( )
{
char ch;
scanf( "%c",&ch );
if( ch!='.' )
{
reverse( );
printf( "%d",ch );
}
}
上面这个函数的功能是读入一串任意长度的字符串,该字符串以'.'结束,并打印出该字符串的倒序。
-----------------
下面是一个函数的定义:
|x-10 x>100 递归出口
f(x) = |
|f(f(x+11) x<=100 递归体
递归求值:
int m( int x )
{
int temp;
if( x>100 )
return ( x-10 );
else
{
temp=m( x+11 );
return m( temp );
}
}
非递归球值:
int fun( int x )
{
int num=1,temp;
if( x>100 )
return ( x-10 );
else
{
num ++ ;
temp = x+11;
}
while( num>0 )
{
if( temp > 100 )
{
num --;
temp = temp-10;
}
else
{
num ++;
temp = temp+11;
}
}
}
-----------------------------------------------------

下面举一个例子,分别用递归和非递归来前序遍历二叉树:
其中二叉树的结点定义为:
typedef struct node
{
char data;
struct node *left,*right;
} tree;
递归:
void pro( tree * t )
{
if( t!=NULL )
{
printf( "%c",t->data );
pro( t->left );
pro( t->right );
}
}
非递归:
void pro( tree * t)
{
tree stack[100],node;
int top;
stack[top] = t; //将根结点入栈
while( top>0 )
{
node = stack[top] //出栈
top --;
printf( "%c",node->data );
if( node->right!=NULL ) //若右子树为空,则将其根结点入栈
{
top ++;
stack[top] = node->right;
}
else //若左子树不为空,则将其根结点入栈
{
top ++;
stack[top] = node->left;
}
}
}
...全文
305 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
jangirl 2004-11-05
  • 打赏
  • 举报
回复
还是不懂怎么解
Wolf0403 2004-11-04
  • 打赏
  • 举报
回复
有一道笔试题:
某人打靶, 10枪打了89环, 让你输出每枪的换数(所有可能的组合)
每枪的成绩介于0-10之间

中止条件:
1、10 枪
2、89 环

限制条件:
1、每枪的成绩介于0-10之间

开始状态:
1、0 枪
2、0 环

变化状态:
1、枪数递增 1
2、环数递增 n ( 0 <= n <= 10 )

分析开就很好写了。
jangirl 2004-11-04
  • 打赏
  • 举报
回复
有一道笔试题:
某人打靶, 10枪打了89环, 让你输出每枪的换数(所有可能的组合)
每枪的成绩介于0-10之间

用递归怎么做呢
ywyw 2004-11-04
  • 打赏
  • 举报
回复
mark
xu123 2004-11-04
  • 打赏
  • 举报
回复
学习...
Wolf0403 2004-11-03
  • 打赏
  • 举报
回复
不客气:)
daylove 2004-11-03
  • 打赏
  • 举报
回复
哈哈,谢谢废人的捧场,,:)
Wolf0403 2004-11-03
  • 打赏
  • 举报
回复
常见的结构化编程语言基本控制结构都包括了循环和选择;两者搭配可以轻松解决许多重复性问题,譬如遍历一个列表,等等。

typedef struct ListNode * Handle;

void process ( const Handle head )
{
Handle curr = head;
while ( curr != NULL )
{
do_process( curr );
curr = curr->next; // (1) 注意这里
}
}

在我标记了 (1) 的地方可以看到,curr 在循环遍历过程中在不断变化,依次指向链表中的每个节点。换言之,curr 在遍历过程中保存了一个“状态”信息。修改 curr 的状态就是遍历过程的一个“副作用”。

有时候这个副作用很有用——所有“传统”的编程语言都是建立在这个基础之上的——有时候不。譬如我们要【反向】遍历一个单链表,或者【上溯】一个二叉树。一旦副作用让我们的“焦点”转移,我们就再也没法“回去”了。这个时候,递归的优势就体现出来了。

void process ( const Handle curr )
{
do_process( curr );
process( curr->next );
}
do_process( curr ); 在前,就是正序遍历;如果改成 process( curr->next );,就轻松变成了逆序。因为,每次调用 process,编译器都会在栈上创造一个 curr 的副本。这个副本从创建到撤销都不会被改变。因此,只要从当前函数 return,我们就可以找回上一个 curr 的状态,而不像具有副作用的语句那样,每次赋值都会取消上一个状态。

可以看看 daylove 老大的非递归版本前序遍历二叉树的方法:他用一个 stack 来依次保存每个状态,因此就是把递归中编译器的工作改成了手工而已。
wasoxi 2004-11-02
  • 打赏
  • 举报
回复
先顶一下再看
dot99 2004-11-02
  • 打赏
  • 举报
回复
沙发(专业术语)~~~
ringbellxxh 2004-11-02
  • 打赏
  • 举报
回复
有用
_青云_ 2004-11-02
  • 打赏
  • 举报
回复
不错,顶!!!
ScorpioCool 2004-11-02
  • 打赏
  • 举报
回复
分解复杂问题的时候递归非常方便……呵呵
Kenny_Glacier 2004-11-02
  • 打赏
  • 举报
回复
呵呵,及时,我刚学到着,就当预习了,呵呵~~~~~~~~!!
顶一下~~~~~!!

33,311

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 新手乐园
社区管理员
  • 新手乐园社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧