二叉树的各种遍历实现伪代码

来源:互联网 发布:阿里云ip段 编辑:程序博客网 时间:2024/05/18 21:49
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
 

最近需要复习一下基础算法,今天先来个二叉树的几种遍历方式的伪代码吧。
其中每种遍历方式都分为递归和非递归两种。

下面的代码我没有进行优化,可能代码有些冗余,不够简洁。因为没有真正的进行严格的测试,只是在纸上画了话,走了一下流程,欢迎大家指正错误。在代码中使用access作为访问节点数据的方式。其实最好的方法是将access作为类型为函数指针的参数,但是今天只为了温习一下实现,而不是真正的去写代码。就按照简单的来了。

对于非递归的实现,在上学的时候,当时并没有真正理解为什么要这样实现,来消除递归。现在我会把将递归转为非递归的过程描述得清楚一些。

1. 先序遍历:先访问根节点,然后遍历左子树,最后遍历右子树
a) 递归算法
  1. void preorder_traverse(const struct bi_tree *tree)
  2. {
  3.     if (tree) {
  4.         /* 访问根节点*/
  5.         access(tree->data);

  6.         if (tree->left) {
  7.             /* 访问左节点 */
  8.             preorder_traverse(tree->left);
  9.         }

  10.         if (tree->right) {
  11.             /* 访问右节点 */
  12.             preorder_traverse(tree->right);
  13.         }
  14.     }
  15. }
递归实现太简单了,而且非常清晰,便于理解。

b) 非递归算法
  1. void preorder_traverse(const struct bi_tree *tree)
  2. {
  3.     /*
  4.     为什么要选择用栈呢。
  5.     当我们遍历先序遍历二叉树时,按照先序的定义,先根节点,然后左节点,然后在右节点。
  6.     这样当按照不断地访问左节点而向下时,为了以后打印右节点,所以需要保存右节点。
  7.     之所以选择栈保存,因为打印右节点顺序与栈的特性先进后出相符,所以选择栈来保存右节点。
     后面的其他遍历方式,同样因为上面这个原因选择栈作为非递归遍历的一个保存方式。
  1.     */
  2.     struct stack s;

  3.     init_stack(&s);

  4.     const struct node *n = tree;

  5.     while (n) {
  6.         /* 访问节点 */
  7.         access(n->data);
         
         /* 该节点有左子节点,需要不断的往下遍历 */
  1.         while (n->left) {
  2.             /* 有右节点,那么将右节点压栈 */
  3.             if (n->right) {
  4.                 s.push(n->right);
  5.             }
  6.             
  7.             /* 移到左节点 */
  8.             n = n->left;
  9.             /* 访问该节点,在这里也可看作新的根节点 */
  10.             access(n->data);
  11.         }
       
         /* 开始访问右节点 */
         if (n->right) {
             /* 移到右节点 */
             n = n->right;
         }
         else {
             /* 需要弹出上一个保存的右节点 */
             n = s.pop();
         }
  1.     }
  2. }

2. 中序遍历:先遍历左子树,然后根节点,最后是右子树
a) 递归算法
  1. void midorder_traverse(const struct bi_tree *tree)
  2. {
  3.     if (tree->left) {
  4.         midorder_traverse(tree->left);
  5.     }
  6.     access(tree->data);
  7.     if (tree->right) {
  8.         midorder_traverse(tree->right);
  9.     }
  10. }
递归实现就是简单啊

b) 非递归算法
  1. void midorder_traverse(const struct bi_tree *tree)
  2. {
  3.     struct stack s;

  4.     init_stack(&s);

  5.     const struct node *n = tree;
  6.     
  7.     while (n) {
         /* 不断的沿左节点向下走 */
  1.         while (n->left) {
  2.             s.push(n);
  3.             n = n->left;
  4.         }
          
         /* 访问节点*/
  1.         access(n->data);
         
         /* 弹出父节点 */
  1.         while (n = s.pop()) {
  2.             access(n->data)
  3.             if (n->right) {
  4.                 /* 如果父节点有右节点,那么需要再次检查该右节点的左节点*/
  5.                 break;
  6.             }
  7.             /* 没有右节点,继续弹出保存的节点 */
  8.         }        
  9.     }
  10. }

3. 后序遍历:先左节点,然后右节点,最后根节点
a) 递归算法
  1. void lastorder_traverse(const struct bi_tree *tree)
  2. {
  3.     if (tree->left) {
  4.         lastorder_traverse(tree->left);
  5.     }
  6.     if (tree->right) {
  7.         lastorder_traverse(tree->right);
  8.     }
  9.     access(tree->data);
  10. }
不得不感叹递归的实现太简单了。。。

b) 非递归算法
之前的写法有错误,当从栈中弹出节点时,且该节点又有右子节点时,需要再次尝试先遍历该右子节点的所有左子节点时,会将该节点丢失。
那么,这时需要重新将该节点压栈,但是当再次弹出时,需要区分其右子节点是否已经遍历过了。所有在节点中添加了一个成员变量access。当被access函数访问时,该access被置位。

还有一个想法,看是否能够避免引入这个成员变量,暂时还没有答案。

  1. void lastorder_traverse(const struct bi_tree *tree)
  2. {
  3.     struct stack s;

  4.     init_stack(&s);

  5.     const struct node *n = tree;
  6.     
  7.     while (n) {
  8.         /* 沿左节点向下走 */
  9.         while (n->left) {
  10.             s.push(n);
  11.             n = n->left;
  12.         }

  13.         do {
  14.             /* 如果有右节点,仍然需要检查左节点 */
  15.             if (n->right && !n->right->access) {
  16.                 /* 右节点还未访问过,需要把该节点再次压栈 */
  17.                 s.push(n);
  18.                 n = n->right;
  19.                 break;
  20.             }
             
             /* 没有右节点,访问节点, 并弹出上一个父节点 */
  1.             access(n->data);
  2.         } while (n = s.pop());
  3.     }
  4. }

4. 按层访问:从根节点,由上及下,由左向右访问节点
a) 递归算法:
这个。。。这个。。。似乎没有递归的用武之地呵。。。

b) 非递归算法:
  1. void layorder_traverse(const struct bi_tree *tree)
  2. {
  3.     /*
  4.     这里选用queue而非stack,因为这种遍历方式需要的是FIFO,即先进先出 
  5.     */
  6.     struct queue q;

  7.     init_queue(&q);

  8.     const struct node *n = tree;

  9.     while (n) {
  10.         /* 访问节点 */
  11.         access(n->data);

  12.         if (n->left) {
  13.             /* 将左节点放入队列 */
  14.             q.push(n);
  15.         }

  16.         if (n->right) {
  17.             /* 将右节点放入队列 */
  18.             q.push(n);
  19.         }
         
         /* 取出下一个节点 */
  1.         n = q.pop();
  2.     }
  3. }

再次说明,以上代码未经测试,只是我在纸上简单的画了画,想了一下流程。思路基本上应该没有什么问题,再次欢迎大家指正。

<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(275) | 评论(0) | 转发(0) |
0

上一篇:从7个方面简单学习shell的正则表达式

下一篇:c语言宏定义总结

相关热门文章
  • A sample .exrc file for vi e...
  • IBM System p5 服务器 HACMP ...
  • 游标的特征
  • DB2 9 应用开发(733 考试)认...
  • busybox的httpd使用CGI脚本(Bu...
  • linux dhcp peizhi roc
  • 关于Unix文件的软链接
  • 求教这个命令什么意思,我是新...
  • sed -e "/grep/d" 是什么意思...
  • 谁能够帮我解决LINUX 2.6 10...
给主人留下些什么吧!~~
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 退休时档案丢了怎么办 职工与企业没有劳资怎么办 去大学报道的档案袋丢失怎么办 档案入学毕业年份写错怎么办 从事业单位辞职后人事档案怎么办 老师辞职不给批怎么办 公办教师去私立学校档案怎么办 辞职后档案不给怎么办 档案不小心拆了怎么办 退休职工档案年龄有涂改怎么办 养老金原始档案找不到怎么办退休 寄辞职信不接收怎么办 公司不给办离职怎么办 离职手续表填写错误怎么办 退货少退了个配件怎么办 小米8拖影严重怎么办 被兼职中介骗了怎么办 被兼职中介坑了怎么办 人在工厂宿舍死了怎么办 事业单位在编人员开除后社保怎么办 因违规无法进群怎么办 微信号违规进不了群怎么办 工作跨省调动社保怎么办 工作中看到别人违反规定应该怎么办 深户调令过期了怎么办 特岗教师满三年怎么办 特岗教师想辞职怎么办 入职一周想离职怎么办 原单位买断工龄后档案怎么办 北京国企辞职后户口怎么办 工作档案弄丢了怎么办 沈阳大集体职工工龄漏算怎么办 集体职工工龄漏算怎么办 cad打开字体是问号怎么办 cad中字体显示问号怎么办 代扣代缴个税申报逾期申报怎么办 个税申报错了怎么办 个税公司报错了怎么办 个税为0没申报怎么办 建筑老项目无法取得发票怎么办 客户说选的地砖不好看怎么办