递归写法总结

先贴出Eric Lippert在stackoverflow的递归写法:

http://stackoverflow.com/questions/9304469/how-to-perform-a-recursive-search/9314805#9314805

其中写到递归大致模板:

Result M(Problem prob)
{
    if (<problem can be solved easily>)
        return <easy solution>;
    // The problem cannot be solved easily.
    Problem smaller1 = <reduce problem to smaller problem>
    Result result1 = M(smaller1);
    Problem smaller2 = <reduce problem to smaller problem>
    Result result2 = M(smaller2);
    ...
    Result finalResult = <combine all results of smaller problem to solve large problem>
    return finalResult;
}

举出二叉树深度的例子套用以上公式

int Depth(Tree tree)
{
    // Start with the trivial case. Is the tree empty?
    if (tree.IsEmpty) return 0;
    // The tree is not empty. 
    // Reduce the problem to two smaller problems and solve them:
    int depthLeft = Depth(tree.Left);
    int depthRight = Depth(tree.Right);
    // Now combine the two solutions to solve the larger problem.
    return Math.Max(depthLeft, depthRight) + 1;
}

其又总结了要想写出递归所必备的三个条件:

  1. The problem has to get smaller every time you recurse.
  2. The problem has to eventually get so small that it can be solved without recursion
  3. The problem has to be solvable by breaking it down into a series of smaller problems, solving each one, and combining the results.

我自己的理解如下:

递归模板大致可以是:

T fun(T  n)
    {
        if (边界条件)
            return T的对象 //(这里不是看n,而是看函数的作用是什么,它要返回什么,该返回对象与返回类型T有关,也有可能是null)
        else
        {
            //if(可能的其他边界条件) 
            T obj = fun(//更小的事件);//可能不止一个递归方程,如求左右子树时
            return obj;
        }
    }
  1. 函数类型T 是void型

此时实际上述模板的两个return都可以保留写成:

T fun(T  n)
    {
        if (//边界条件)
            returnelse
        {
            //if(可能的其他边界条件) 
            fun(//更小的事件);//可能不止一个递归方程,如树时有左右子树,要两个
            return;
        }
    }

当然,此时else中的return一般不写,因为,else中的语句运行完,隐式的就有return了,写出来只是显示地表达出递归的返回含义

  1. 边界条件
    第一个边界条件就是考虑递归往下到最后一个结点时的情况,若在递归中(else语句中)还存在其他边界条件,可正面写,如
if (root != NULL)
    fun();

而不写成

if (root == NULL) return //返回void或对象 ;
else  fun();

这样在else语句中只会出现一个return语句

参考剑指offer面试题重建二叉树

BinaryTreeNode* ConstructCore
(
    int* startPreorder, int* endPreorder, 
    int* startInorder, int* endInorder
)
{
    // 前序遍历序列的第一个数字是根结点的值
    int rootValue = startPreorder[0];
    BinaryTreeNode* root = new BinaryTreeNode();
    root->m_nValue = rootValue;
    root->m_pLeft = root->m_pRight = NULL;

    if(startPreorder == endPreorder)
    {
        if(startInorder == endInorder && *startPreorder == *startInorder)
            return root; //最后一个结点endNode返回后就到上一级的函数了
        else
            throw std::exception("Invalid input.");
    }

    // 在中序遍历中找到根结点的值
    int* rootInorder = startInorder;
    while(rootInorder <= endInorder && *rootInorder != rootValue)
        ++ rootInorder;

    if(rootInorder == endInorder && *rootInorder != rootValue)
        throw std::exception("Invalid input.");

    int leftLength = rootInorder - startInorder; //左子树的节点个数
    int* leftPreorderEnd = startPreorder + leftLength;
    if(leftLength > 0)
    {
        // 构建左子树
        root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd, 
            startInorder, rootInorder - 1);
    }
    if(leftLength < endPreorder - startPreorder)
    {
        // 构建右子树
        root->m_pRight = ConstructCore(leftPreorderEnd + 1, endPreorder,
            rootInorder + 1, endInorder);
    }

    return root;
}

感想:

  1. 简单题目一般都可以用尾递归解决,复杂一些就不能把递归语句放在最后的return前面了(尾递归时,递归语句等价于结束前的循环语句)。
  2. 递归形式大部分都可以看成,一个结点()与剩余一整串的关系,然后该剩余一整串的处理采用递归方法。
  3. 递归语句写出后,其余语句的写法按照正常思维进行即可。

  4. 函数类型及作用是返回什么,递归语句相应的一定要返回什么,按格式写一般都不会出错,如:

 fun(//更小的事件);  //T是void
 T obj = fun(//更小的事件);  //T非void

举例: 从尾到头打印链表

void PrintListReversingly_Recursively(ListNode* pHead)
{
    if (pHead == NULL)
        return;
    else
    {
        PrintListReversingly_Recursively(pHead->m_pNext);
        printf("%d\t", pHead->m_nValue);
    }

}

分析:
1. 其中 PrintListReversingly_Recursively(pHead->m_pNext); 这句可以看成已经完成了pHead->m_pNext为首的后续结点的反转工作,原理在于递归的每个结点的PrintListReversingly_Recursively(pHead->m_pNext)实际都包含了打印当前结点的下一个节点:printf("%d\t", pHead->m_nValue);从而我们可以直接把递归语句看成就是完成了后续pHead->m_pNext为首的链表所有工作。现在就把问题分为只剩链表头一个结点pHead和剩余的一整串以pHead->m_pNext为首的链表了,现在就可以用顺序的思维写他们的关系了,而他们的关系是逆序打印,故递归语句先写,pHead后写。
2. 只有当前问题与更小规模问题的处理方法一致时才能使用递归,处理方法即使除递归外的语句,如printf("%d\t", pHead->m_nValue);

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值