zoukankan      html  css  js  c++  java
  • 再谈递归

      我们似乎记得有这么一个观点”递归都可以通过迭代(循环)实现,但是迭代不一定能够通过递归实现“。那么我们是否思考过这其中的道理呢?

    一、递归概念

      对于什么是递归,众说纷纭。我个人比较认可的一种简单直接的文字描述是:先递进,再回归;还有一种是动图描述:一只兔子拿着一面镜子,镜子里面和镜子的镜子的...镜子里面还是相同的场景,循环不止。当然,递归可不能这么无限下去,容易栈溢出,所以,递归得有出口。

    二、什么时候用递归

      第一次接触递归的例子是斐波拉契函数:F(n) = F(n - 1) + F(n - 2), F(1) = F(0) = 1; n >= 2。对该函数进行分析不难发现,如果我们想求解一个大的斐波拉契函数值m,我们必须得先不断递进到我们能够获取到的函数值,例如:F(1) = F(0) = 1。然后再回归,依次求解F(2) = F(1) + F(0); F(3) = F(2) + F(1); ... ....; F(m)  = F(m - 1) + F(m - 2)。

      代码如下:

    1 int F(int n)
    2 {
    3      if(n == 0 || n == 1){
    4          return 1; 
    5      }
    6       return F(n - 1) + F(n - 2);   
    7 }

      所以,斐波拉契拥有完美的递归性,非常适合使用递归实现。此时,递归出口:n = 0 或 n = 1的时候返回1,递归条件:F(n - 1) + F(n - 2);当然迭代也能够完美实现该函数。到这里,我们就明白了,能够迭代实现的不一定拥有递归性,比如:递归出口。我们可以这么通俗的理解递归:递归是一种将复杂问题逐步分解为小的相同问题,直到分解到我们能够求解的最小问题为止,每个次小问题的解都给予更小问题的解,最后回归到复杂问题的解的方法。一般能够用数学公式表示的,可通过数学公式知道是否具有递归性;但是有时候我们的问题并不能单纯的通过数学公式表示,也可能具有递归性,比如汉诺塔。

    三、哪些数据结构拥有递归性

      拥有完美递归性的数据结构里面想到的是二叉树。例如:

                          A
                       /      
                      B       C
                    /       /   
                   D    E   F

      从根节点A看,就是一个二叉树;从A的左子节点B或右子节点C向下看都是一颗二叉树。其实,链表也拥有递归性。例如:

    -----------    -----------   -----------
    | A | next|--> | B | next|-->| C | null|
    -----------    ------------  -----------

      当我们继续向尾部插入节点D的时候,我们可以将A --> B -->C这个链表当成一个头节点head,只有找到这个头节点,然后head->D即可。

    四、二叉树和链表递归实现

      二叉树数的很多操作都可以直接用递归实现,代码逻辑简单,最典型的就是深度遍历,如下:

     1 // Root-Left-Right
     2 void PrevOrder(Node* root)
     3 {
     4     if(root == nullptr){
     5         return;
     6     }
     7     std::cout  << root->data << ",";
     8     PrevOrder(root->left);
     9     PrevOrder(root->right);
    10 }
    11 
    12 // Left-Root-Right
    13 void Inorder(Node* root)
    14 {
    15     if(root == nullptr){
    16         return;
    17     }
    18     Inorder(root->left);
    19     std::cout << root->data << ",";
    20     Inorder(left->right);
    21 }
    22 
    23 // Left-Right-Root
    24 int PostOrder(Node* root)
    25 {
    26     if(root == nullptr){
    27         return;
    28     }
    29     PostOrder(root->left);
    30     PostOrder(root->right);
    31     std::cout << root->data << ",";
    32 }
    

      单链表尾插递归算法:

    1 Node* PushBack(Node* head, int data)
    2 {
    3     // 带头结点的链表
    4     if(head->next == nullptr){
    5        return new Node(data);
    6     }
    7     head->next = PushBack(head->next, data);
    8     return head;
    9 }

      单链表倒置递归算法:

     1 Node* Invert(Node* head)
     2 {
     3     if(head->next == nullptr){
     4         return head;
     5     } 
     6     auto newHead = Invert(head->next); // head--> newHead
     7    
     8     newHead->next = head;  // head <--> newHead
     9     head->next = nullptr;      // head<-- newHead
    10     return newHead;
    11 }

      上面迭代法的逻辑也相对比较简单,感兴趣的同学可以自行实现。

      上面是我们对递归算法的个人看法,主要是方便个人回顾之用,也希望其他读者能够对递归的认识起到一点理解的作用!

  • 相关阅读:
    mac 10.15.7 修改PATH
    oc 属性类型一般用法
    ubuntu解压zip文件名乱码
    telnet 退出
    docker 根据容器创建镜像
    mac android adb device 没有显示设备
    Yii2 查看所有的别名 alias
    Yii2 App Advanced 添加 .gitignore
    ubuntu 18.04 搜狗突然就提示乱码
    An error occured while deploying the file. This probably means that the app contains ARM native code and your Genymotion device cannot run ARM instructions. You should either build your native code to
  • 原文地址:https://www.cnblogs.com/smartNeo/p/14749208.html
Copyright © 2011-2022 走看看