• 再谈递归


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

    一、递归概念

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

    二、什么时候用递归

      第一次接触递归的例子是斐波拉契函数: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 }

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

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

  • 相关阅读:
    我爬取了爬虫岗位薪资,分析后发现爬虫真香
    红薯,撑起父亲的快乐,让我揪心
    跨域问题服务端解决办法 Request header field Authorization is not allowed by Access-Control-Allow-Headers
    antdvue2.x 使用阿里iconfont自定义组件iconfont
    前端 crypto-js aes 加解密
    jsencrypt加密解密字符串
    CryptoJS base64使用方法
    客户端js生成rsa 密钥对
    js动态添加style样式
    PHP 使用非对称加密算法(RSA)
  • 原文地址:https://www.cnblogs.com/smartNeo/p/14749208.html
Copyright © 2020-2023  润新知