• 二叉树遍历算法


    首先,个人认为,二叉树是很能体会递归算法思想的,因为二叉树的结构是leftTree->root<-rightTree,对于每个非叶子节点,该规律都适用,因此关于二叉树的很多算法也都能用递归思想搞定。递归的优点在于代码简洁,但效率却是问题。其次,对于各种顺序的遍历,又有着相应的非递归算法,非递归算法的代码量相对大一点,但更容易掌控,而且效率更优。

    先看节点结构:

    1 struct Bitree{
    2   int val;
    3   Bitree *left, *right;
    4   Bitree(int x):val(x), left(nullptr), right(nullptr){
    5   };
    6 };

    1. 中序遍历

    •  递归算法

    显然,中序遍历的顺序为leftTree, root, rightTree,显然先遍历左子树,然后是根,最后右子树。中序遍历的递归算法自然也就出来了。

     1 void inOrderTraverse1(Bitree *root){
     3   if(root){
     5     inOrderTraverse1(root->left);
     7     visit(root);
     9     inOrderTraverse1(root->right);
    11   }
    13 }
    • 非递归算法1.

    非递归算法的思想也比较简单,按从左到右进行访问。先指针往左探寻到底,然后观察最后一个非空节点是否有右节点,若有,将该右节点作为新的探寻起点,再进行下一轮的探寻。显然,“一探到底”的思路需要使用stack来帮助缓存之前的节点。

     1 void inOrderTraverse2(Bitree *root){
     2   stack<Bitree *> S;
     3   S.push(root);
     4   Bitree *p = root;
     5   while(!S.empty()){
     6     p = S.top();
     7     while(p){
     8       S.push(p->left);
     9       p = p->left;
    10     }
    11     S.pop();//pop out the nullptr
    12     if(!S.empty()){
    13       p = S.top();
    14       visit(p);
    15       S.pop();
    16       S.push(p->right);//push its right child into the stack
    17     }
    18   }
    19 }
    • 非递归算法2
     1 void inOrderTraverse3(Bitree *root){
     2   stack<Bitree *> S;
     3   Bitree *p = root;
     4   while(p || !S.empty()){
     5     if(p){
     6       S.push(p);
     7       p = p->left;
     8     }else{
     9       p = S.top();
    10       visit(p);
    11       S.pop();
    12       p = p->right;
    13     }
    14   }
    15 }

    个人认为,虽然两种非递归算法的思路完全一样,但非递归算法2比非递归算法1代码要更为简洁,更值得推荐。

    2. 前序遍历

    • 递归算法

    前序遍历的顺序为root,leftTree,rightTree,直接上代码

    1 void preOrderTraverse1(Bitree *root){
    2   if(root){
    3     visit(root);
    4     preOrderTraverse1(root->left);
    5     preOrderTraverse1(root->right);
    6   }
    7 }
    • 非递归算法1

    对于前序遍历的非递归算法,和中序遍历的非递归算法非常相似,不过是在进栈时就访问该节点,而不是之后再访问。由于代码相似,先给出一种

     1 void preOrderTraverse2(Bitree *root){
     2   stack<Bitree *> S;
     3   Bitree *p = root;
     4   while(p || !S.empty()){
     5     if(p){
     6       visit(p);
     7       S.push(p);
     8       p = p->left;
     9     }else{
    10       p = S.top();
    11       S.pop();
    12       p = p->right;
    13     }
    14   }
    15 }
    •  非递归算法2

    该算法采用了和前序遍历相同的思想,即root节点先进栈,root节点出栈时,将其右节点先进栈,然后是左节点进栈。这样,利用栈先进后出的性质,访问顺序自然变为了root,左子树,右子树。

     1 void preOrderTraverse3(Bitree *root){
     2   if(!root){
     3     return;
     4   }
     5   stack<Bitree *> S;
     6   Bitree *p = root;
     7   S.push(root);
     8   while(!S.empty()){
     9     p = S.top();
    10     visit(p);
    11     S.pop();
    12     if(p->right){
    13       S.push(p->right);
    14     }
    15     if(p->left){
    16       S.push(p->left);
    17     }
    18   }
    19 }

    3. 后续遍历

    • 递归算法

    后续遍历的顺序是leftTree,rightTree和root,因此递归算法也自然出来了

    1 void postOrderTraverse1(Bitree *root){
    2   if(root){
    3     postOrderTraverse1(root->left);
    4     postOrderTraverse1(root->right);
    5     visit(root);
    6   }
    7 }
    • 非递归算法1

    和之前中序和前序算法不同,后续遍历的root节点要最后才能被访问,因此,我们若想访问某节点,那么我们需要知道该节点的右节点是否已经被访问过。只有该节点的右节点为null,或者已被访问过,那么该节点才能被访问;否则需要先将右节点访问完。为了判断该节点的右节点是否已经被访问过,需另外设一个记录指针last来指示已经访问过的节点,如果之前访问过的节点last恰为该节点的右节点,说明其右子树已经访问完,应该访问该节点。

     1 void postOrderTraverse2(Bitree *root){
     2   Bitree *last = nullptr;
     3   Bitree *p = root;
     4   stack<Bitree *> S;
     5   while(p || !S.empty()){
     6     while(p){
     7       S.push(p);
     8       p = p->left;
     9     }
    10     p = S.top();
    11     if(p->right && p->right != last){
    12       p = p->right;
    13     }else{
    14       visit(p);
    15       S.pop();
    16       last = p;
    17       p = nullptr;//p needs to be updated to null for next loop
    18     }
    19   }
    20 }

    tip 1:后续遍历中,root节点最后才能被访问到,因此,栈能记录每一个节点的路径,包括叶子节点。这一点性质可用于求解和树的路径有关的问题。

    •  非递归算法2

    和前序遍历的非递归算法2一样,这里也给出后续遍历对应的非递归算法2,思路也是类似。由于后序遍历中,根节点要最后才能被访问到,不像前序遍历中刚访问到便可以输出。但在实际查找过程中,我们又只能先从根节点开始查找,才能接着查找左子树和右子树,由此可以再利用栈先进后出的特性来存储根节点。

     1 void postOrderTraverse3(Bitree *root){
     2   if(!root){
     3     return;
     4   }
     5   Bitree *p = root;
     6   stack<Bitree *> S;
     7   stack<Bitree *> postOrder;
     8   S.push(p);
     9   while(!S.empty()){
    10     p = S.top();
    11     postOrder.push(p);
    12     S.pop();
    13     if(p->left){
    14       S.push(p->left);//first IN, later OUT
    15     }
    16     if(p->right){
    17       S.push(p->right);//later IN, first OUT
    18     }
    19   }
    20   while(!postOrder.empty()){
    21     p = postOrder.top();
    22     visit(p);
    23     postOrder.pop();
    24   }
    25 }

    4. 层序遍历

    层序遍历的思想和之前三种遍历方式不同,需要借助queue来对节点进行缓存,先进队列的节点需要先离开。这和图的BFS思想一样,毕竟树本质上也是一种特殊的图。

     1 void levelOrderTraverse(Bitree *root){
     2   if(!root){
     3     return;
     4   }
     5   queue<Bitree *> Q;
     6   Bitree *p = nullptr;
     7   Q.push(root);
     8   while(!Q.empty()){
     9     p = Q.front();
    10     Q.pop();
    11     visit(p);
    12     if(p->left){
    13       Q.push(p->left);
    14     }
    15     if(p->right){
    16       Q.push(p->right);
    17     }
    18   }
    19 }

    Tip 2: 层序遍历思想简单,利用queue来一层一层输出。因此,可用于求解数的宽度和高度。

  • 相关阅读:
    fatal: unable to access 'https://github.com/github-eliviate/papers.git/': Failed to connect to github.com port 443 after 21107 ms: Timed out
    ImportError: attempted relative import with no known parent package
    python 创建中文目录
    pyodbc.InterfaceError: ('IM002', '[IM002] [Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序 (0) (SQLDriverConnect)')
    pyodbc.InterfaceError: ('28000', '[28000] 用户 'sa' 登录失败。 (18456) 无法打开登录所请求的数据库 "ARCHIVEDB"。登录失败。
    pyodbc.OperationalError: ('08001', '[08001] 无法打开与 SQL Server 的连接[08001]登录超时已过期 (0); 与 SQL Server 建立连接时发生了与网络相关的或特定于实例的错误。找不到或无法访问服务器。请检查实例名称是否正确以及 SQL Server 是否配置为允许远程连接。
    医学图像分割论文:Swin-Unet—Unet-like Pure Transformer for Medical Image Segmentation_202105.05537
    网络请求例子
    blog.devtang.com
    nsdata
  • 原文地址:https://www.cnblogs.com/wuiCoding/p/6670620.html
Copyright © 2020-2023  润新知