• 二叉树的遍历


    Ⅰ、二叉树基本介绍

             

    1.1、二叉树的定义

    二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。

    二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树 。 

    1.2、特殊的二叉树

    1、满二叉树:如果一棵二叉树只有度为0的节点和度为2的节点,并且度为0的节点在同一层上,则这棵二叉树为满二叉树

     

    2、完全二叉树:深度为k,有n个节点的二叉树当且仅当其每一个节点都与深度为k的满二叉树中编

    号从1到n的节点一一对应时,称为完全二叉树

    完全二叉树的特点是叶子节点只可能出现在层序最大的两层上,并且某个节点的左分支下子孙的最

    大层序与右分支下子孙的最大层序相等或大1 。

    Ⅱ、二叉树的性质

    性质1:二叉树的第i层上至多有2i-1(i≥1)个节点 。

    性质2:深度为h的二叉树中至多含有2h-1个节点

    性质3:若在任意一棵二叉树中,有n0个叶子节点,有n2个度为2的节点,则必有n0=n2+1

    性质4:具有n个节点的满二叉树深为log2n+1。

    性质5:若对一棵有n个节点的完全二叉树进行顺序编号(1≤i≤n),那么,对于编号为i(i≥1)的节点:

    当i=1时,该节点为根,它无双亲节点 。

    当i>1时,该节点的双亲节点的编号为i/2 。

    若2i≤n,则有编号为2i的左节点,否则没有左节点 。

    若2i+1≤n,则有编号为2i+1的右节点,否则没有右节点 。


    Ⅲ、二叉树的四种遍历方式

    如果二叉树中元素数目为n。这四种遍历算法的空间复杂性均为O (n),时间复杂性为O(n)。

    3.1、二叉树的前序遍历

    遍历顺序:跟 左 右 

    前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根

    节点,然后遍历左子树,最后遍历右子树。

    这个二叉树前序遍历的结果是:1 2 3 4 5 6

    但是实际上是:1 2 3 null null null 4 5 null null 6 null

    我们为了看上去方便就省去了null.

    代码实现:

    1. void PrevOrder(BTNode* root) {
    2. if (root == NULL) {
    3. printf("NULL ");
    4. return;
    5. }
    6. printf("%d ", root->val);
    7. PrevOrder(root->left);
    8. PrevOrder(root->right);
    9. }

    3.2、中序遍历

    遍历顺序:左 根 右

    中序遍历(LDR)是二叉树遍历的一种,也叫做中根遍历、中序周游。在二叉树中,中序遍历首先

    遍历左子树,然后访问根结点,最后遍历右子树。

    中序遍历结果:3 2 1 5 4 6

    实际上的结果应该是:null 3 null 2 null 1 null 5 null 4 null 6 null

    代码实现:

    1. void InOrder(BTNode* root)
    2. {
    3. if (root == NULL)
    4. {
    5. printf("NULL ");
    6. return;
    7. }
    8. InOrder(root->left);
    9. printf("%d ", root->val);
    10. InOrder(root->right);
    11. }

    3.3、后序遍历

    遍历顺序:左 右 跟

    后序遍历首先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历

    左子树,然后遍历右子树,最后遍历根结点。

    后续遍历结果:3 2 5 6 4 1

    实际上应该是:null null 3 null 2 null null 5 null null 6 4 1 

    代码实现:

    1. void PostOrder(BTNode* root)
    2. {
    3. if (root == NULL)
    4. {
    5. printf("NULL ");
    6. return;
    7. }
    8. PostOrder(root->left);
    9. PostOrder(root->right);
    10. printf("%d ", root->val);
    11. }

    3.4、层次遍历

    遍历顺序:一层一层的遍历

    二叉树的层次遍历 ,顾名思义就是指从二叉树的第一层(根节点)开始,从上至下逐层遍历,

    同一层中,则按照从左到右的顺序对节点逐个访问。在逐层遍历过程中,按从顶层到底层的次序访

    问树中元素,在同一层中,从左到右进行访问。

    层次遍历结果:1 2 4 3 5 6

    实际遍历结果:1 2 4 3 null 5 6 null null null null null null 

    代码实现:

    1. void levelorder(BinaryTree *tree)
    2. {
    3. Queue q = initQueue();
    4. BinaryTree *temp = tree;
    5. put(&q, temp);
    6. while (!IsEmptyQueue(q))
    7. {
    8. temp = get(&q);
    9. visited(temp);
    10. if (temp->left != NULL)
    11. put(&q, temp->left);
    12. if (temp->right != NULL)
    13. put(&q, temp->right);
    14. }
    15. }

    Ⅳ、二叉树求节点

    4.1、求二叉树总节点个数

    方法一

    我们惯用的方式,就是创建一个变量Size,然后每遍历一个节点Size++。

    这种方式有很大的局限性,就是我们在遍历二叉树时采用的是递归的方式,如果把Size放到函数里面就会出现每次调用时Size都是同一个值,没办法累加。

    这里我们可以采用 静态函数 stati来修饰Size,这样Size只被调用一次,就可以达到累加的效果。

    看代码:

    1. int TreeSize(BTNode* root)
    2. {
    3. static int size = 0;
    4. if (root == NULL)
    5. return 0;
    6. else
    7. ++size;
    8. TreeSize(root->left);
    9. TreeSize(root->right);
    10. return size;
    11. }

    这样我们的得到的结果就是:6。

    但是:当我们再次调用这个函数时我们会不会还得到结果6呢?  答案是不会,他会返回12。因为我们刚才提到静态变量修饰的Size时,Size在整个程序中只会被调用一次。所以这种方式有很大的局限性。我们不妨换个思路来解决

    方法二

    我们还是采用递归的方式,当遍历到叶子节点是我们就让他返回1,一层一层的往上返回,最终在加上1(根节点),我们是不是就可以得到我们整棵树的节点个数了呢?

    看代码:

    1. int TreeSize(BTNode* root)
    2. {
    3. return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
    4. }

    4.2、求二叉树叶子结点的个数

    有了上面求二叉树总节点个数方法二的铺垫,这个问题也就很容易想到了。

    这里我们要怎么确定一个节点时叶子节点呢?很简单没有左右孩子的节点就是叶子节点

    我们可以遍历整个二叉树,遇到叶子节点就返回1,直到遍历到最后我们返回0,把他们全都累加起来,最后结果就是我们想要的叶子结点的个数。

    看代码:

    1. int TreeLeafSize(BTNode* root)
    2. {
    3. if (root == NULL)
    4. return 0;
    5. if (root->left == NULL && root->right == NULL)
    6. {
    7. return 1;
    8. }
    9. return TreeLeafSize(root->left) + TreeLeafSize(root->right);
    10. }

    4.3、求第K层节点的个数

    这个问题相比于上面两个要复杂一点,首先我们要先解决如何遍历到第K层。

    若要求第三层节点的个数:第一层相和第三层差2层,第二层和第三层差1层,第三层和第三层差0层......当k=1时证明就在第k层,我们就返回这个第K层的节点为1

    我们可以得出一个结论:当前树的第K层   =   左子树的第K-1层    +   右子树的第K-1层

    接下来我们来看代码:

    1. int TreeKLevel(BTNode* root, int k)
    2. {
    3. assert(k > 0);
    4. if (root == NULL)
    5. return 0;
    6. if (k == 1)
    7. {
    8. return 1;
    9. }
    10. return TreeKLevel(root->left, k - 1)
    11. + TreeKLevel(root->right, k - 1);
    12. }

    Ⅴ、二叉树相关练习

    1、已知二叉树的前序遍历为5 7 4 9 6 2 1,中序遍历是4 7 5 6 9 1 2则该二叉树的后序遍历是什么?

    答案:4 7 6 1 2 9 5

    解析:

    通过前序遍历找到子树的根,在中序遍历中找到根的位置,然后确定根左右子树的区间,即根的左侧为左子树中所有节点,根的右侧为右子树中所有节点。

    故:根为: 5

    5的左子树:4 7   5的右子树: 6 9  1  2

    5的左子树的根为: 7  5的右子树的根为:9

    7的左子树: 4 7的右:空  9的左子树:6  9的右子树:2

    这棵树的结构是:

    2、如果一颗二叉树的前序遍历的结果是ABCD,则满足条件的不同的二叉树有( )种

    答案:14

    解析:

    首先这棵二叉树的高度一定在3~4层之间:

    三层:

    A(B(C,D),()), A((),B(C,D)), A(B(C,()),D), A(B((),C),D),

    A(B,C(D,())), A(B,C((),D))

    四层:

    如果为四层,就是单边树,每一层只有一个节点,除过根节点,其他节点都有两种选择,在上层节点的左边还是右边,所以2*2*2共8种

    总共为14种。

    也可以由公式的来:,这里的n指的是二叉树结点的个数(5*6*7*8)/(1*2*3*4)/(n+1)

    答案也是14。

    3、一棵非空的二叉树的先序遍历序列与后序遍历序列正好相反,则该二叉树一定满足( )

    答案:只有一个叶子节点

    解析:

    前序遍历:根 左 右

    后序遍历:左 右 根

    从二叉树 前序 和 后序遍历结果规则中可以看出,如果树中每个节点只有一个孩子时,遍历结果肯定是反的

    比如下面这前序和中序序列所构成的树的结构:

    结语:这篇文章讲到这也就结束了, 如有疑问或者质疑,请在评论区发出来,我会尽全力帮大家解决,成长的路上少不了你们的支持,你们的三连是我进步最大的动力,还望大佬们多多支持,一起加油!共勉!

  • 相关阅读:
    在树莓派上控制GPIO常用的编程语言有哪些
    Java 编程问题:四、类型推断
    查看显存和内存大小
    websock
    List集合&UML图
    手把手教你Nginx常用模块详解之ngx_http_gzip_module(四)
    MongoDB-索引-部分索引
    基于头肩部检测的过线客流统计
    JWT有状态登陆与无状态登陆
    正确完成实时 AI
  • 原文地址:https://blog.csdn.net/m0_69323023/article/details/132883499