zoukankan      html  css  js  c++  java
  • 二叉树的创建及各种遍历

    前些天看数据结构,看到了二叉树的相关操作,我觉得,链表搞透彻了,这些东西都不是问题,还有栈、队列之类的,都是基于结构体和指针,其原理和链表相差无几,接下来来分享一下二叉树的创建以及各种遍历方法:

    二叉树简介

    来源于维基百科

     

    二叉树(英语:Binary tree)是每个节点最多只有两个分支(不存在分支度大于2的节点)的树结构。通常分支被称作“左子树”和“右子树”。二叉树的分支具有左右次序,不能颠倒。

    二叉树的第i层至多拥有2^{i-1}个节点数;深度为k的二叉树至多总共有{displaystyle 2^{egin{aligned}k+1end{aligned}}-1}个节点数(定义根节点所在深度{displaystyle k_{0}=0}),而总计拥有节点数匹配的,称为“满二叉树”;

    深度为kn个节点的二叉树,当且仅当其中的每一节点,都可以和同样深度k的满二叉树,序号为1到n的节点一对一对应时,称为“完全二叉树”;

     

    二叉树创建

    首先定义结构体:

    1 typedef struct Tree
    2 {
    3     int data;                         //    数据域
    4     struct Tree *left, *right;        //    左孩子、右孩子
    5 }Tree;
    接着采用递归的方法进行创建、以先序遍历的方法,附上代码:
     1 void create ( Tree* &T)            
     2     int n;
     3     scanf ( "%d", &n );              //    输入节点数据
     4     if ( n == 0 )                    //    也可以改成其他的,这里仅作为空节点的标志
     5     {
     6         T = NULL;
     7     } 
     8     else 
     9     {
    10         T = new Tree;
    11         T -> data = n;
    12         create ( T -> left );    //    递归创建左孩子
    13         create ( T -> right );   //    递归创建右孩子
    14     }
    15 }
    直到补全了所有的节点(无孩子的节点用空节点,这里是0来表示)
     

    这样一个先序遍历创建的二叉树就建立好了,紧接着就是遍历了,通过遍历可以处理一些基本的操作(增、删、改、查);

    二叉树的遍历分为先序遍历、中序遍历、后序遍历以及层次遍历:

    来源于维基百科

    先序遍历:

    先访问根节点、如果有左孩子,继续访问左孩子,然后将左孩子当作下一个根节点继续重复访问,遇到无左孩子(左孩子为空节点)时开始访问其根节点的右孩子;直到所有的否访问完,退出(图中顺序:2->7->2->6->5->11->5->9->4)。

    中序遍历:

    首先判断是否为空二叉树,如果不是,判断是否有左孩子,如果有访问左孩子,如何继续循环判断,如果没有,开始访问相对根节点(即该节点无孩子,访问该节点的父节点),如何访问右孩子;直到遍历结束退出(途中顺序:2->7->5->6->11->2->5->9->4)。

    后序遍历:

    首先判断是否为空,然后判断是否有左孩子,如果有,访问左孩子,并将左孩子当作根节点继续重复,直到无左孩子时,访问其根节点的右孩子,最后访问根节点,直到遍历结束退出(图中顺序:2->5->11->6->7->4->9->5->2)

    层次遍历:

    层次遍历相对来说好理解一点,就是按行访问,第一行访问结束开始访问下一行,图中的顺序为2->7->5->2->6->9->5->11->4 我相信把访问顺序列出来就应该很容易就理解了。虽然好理解,但是实现起来却不容易;

    其实先序中序后续听起来很复杂,弄懂了之后会发现特别简单,先序:跟、左、右;中序:左、根、右;后序:左、右、根;你可以这样理解:按什么顺序,就怎么访问根节点,先序就先访问根节点,中序的话,根节点就放到中间访问,后序就最后访问根节点;理解之后三种顺序搞出来一个,其他两个就So easy 了。
    接下来放代码:

     1 void out1 ( Tree* T )                        //        先序遍历输出 
     2 {
     3     if ( T )
     4     {
     5         printf ( "%d ", T -> data );                    //    先访问根节点
     6         out1 ( T -> left );
     7         out1 ( T -> right );
     8     }
     9 }
    10 void out2 ( Tree* T )                        //    中序遍历输出 
    11 {
    12     if ( T )
    13     {
    14         out2 ( T -> left );                            
    15         printf ( "%d ", T -> data );                    //    中间访问根节点
    16         out2 ( T -> right );
    17     }
    18 }
    19 void out3 ( Tree* T )                        //        后序遍历输出 
    20 {
    21     if ( T )
    22     {
    23         out3 ( T -> left );
    24         out3 ( T -> right );
    25         printf ( "%d ", T -> data );                       //    最后访问根节点
    26     } 
    27 }

    仔细观察不难发现:这三种顺序的代码仅仅是换了位置,代码本身并没有变化,这里的遍历以输出为例,增删改查都是同样的操作。

     1 void out4 ( Tree* T )                        //        层次遍历创建 
     2 {
     3     int rear = -1, front = 0;
     4     Tree *nums[100];
     5     if ( !T )
     6     {
     7         return;
     8     }
     9     nums[front] = T;
    10     while ( front != rear )
    11     {
    12         printf ( "%d ",nums[++rear] -> data );
    13         if ( T -> left )
    14         {
    15             nums[++front] = T -> left;
    16         }
    17         if ( T -> right )
    18         {
    19             nums[++front] = T -> right; 
    20         }
    21         T = nums[rear+1];
    22     }
    23 }  

    怎么样,层次遍历还像你想的那么简单了吗?
    参差遍历我觉得不用递归的简单些,而且层次遍历相对于先序中序后序来说不是重点,大家选择性的看一下,这里的代码中运用到栈的相关思想,我相信看到二叉树的情况下,栈应该都不成问题了。

    最后主函数调用一下即可:

     1 #include "stdio.h"
     2 
     3 int main ( )
     4 {
     5     printf ( "int 型,0 表示空节点:" );
     6     T = new Tree;
     7     create ( T );                            
     8     printf ( "先序遍历输出:" );
     9     out1 ( T );
    10     printf ( "
    中序遍历输出:" );
    11     out2 ( T );
    12     printf ( "
    后序遍历输出:" );
    13     out3 ( T );
    14     printf ( "
    层次遍历输出:" );
    15     out4 ( T );
    16     printf ( "
    " );
    17     return 0;
    18 }
    希望对大家的学习有一定的帮助,欢迎大家批评指正~

  • 相关阅读:
    15. DML, DDL, LOGON 触发器
    5. 跟踪标记 (Trace Flag) 834, 845 对内存页行为的影响
    4. 跟踪标记 (Trace Flag) 610 对索引组织表(IOT)最小化日志
    14. 类似正则表达式的字符处理问题
    01. SELECT显示和PRINT打印超长的字符
    3. 跟踪标记 (Trace Flag) 1204, 1222 抓取死锁信息
    2. 跟踪标记 (Trace Flag) 3604, 3605 输出DBCC命令结果
    1. 跟踪标记 (Trace Flag) 1117, 1118 文件增长及空间分配方式
    0. 跟踪标记 (Trace Flag) 简介
    SpringBoot + Redis + Shiro 实现权限管理(转)
  • 原文地址:https://www.cnblogs.com/nanshaobit/p/12464643.html
Copyright © 2011-2022 走看看