数据结构(十四) -- C语言版 -- 树 - 二叉树的叶子节点、深度、拷贝等

零、读前说明

  • 本文中所有设计的代码均通过测试,并且在功能性方面均实现应有的功能。
  • 设计的代码并非全部公开,部分无关紧要代码并没有贴出来。
  • 如果你也对此感兴趣、也想测试源码的话,可以私聊我,非常欢迎一起探讨学习。
  • 由于时间、水平、精力有限,文中难免会出现不准确、甚至错误的地方,也很欢迎大佬看见的话批评指正。
  • 嘻嘻。。。。 。。。。。。。。收!

一、求二叉树的叶子节点个数

  首先,我们需要明确什么是叶子节点?二叉树的叶子节点是 既没有左子树又没有右子树的特殊的节点

  详细的二叉树的相关的知识点可以参考下面两个文章:

    数据结构(十) – C语言版 – 树 - 基础知识

    数据结构(十一) – C语言版 – 树 - 二叉树基本概念

  二叉树的遍历是操作二叉树的基础,二叉树的很多特性可以通过遍历二叉树的过程得到,在实际应用中,统计二叉树的叶子节点的个数是比较常见的一种操作。

  二叉树的叶子节点个数的操作可以使用先序遍历,中序遍历,后序遍历中的任何一种,只需要在将访问操作变成判断该节点是否为叶子节点(既不存在左孩子、也不存在右孩子的节点)

    如果是叶子节点,那么累加和加一
    如果不是叶子节点,则继续递归判断

  本文章中使用的是先序的方式进行统计,有兴趣的小伙伴可以试一下其他的方式(其实就是将判断的部分代码移动一下就行了)。下面是代码。

/**
 * 功 能:
 *      求出二叉树的叶子节点的个数
 * 参 数:
 *      root:要操作的树
 *      num :叶子节点的个数的返回值
 *            注意:不能为NULL!
 * 返回值:
 *      成功:叶子节点的个数
 *      失败:-1 
 **/
int CounterBiTreeLeaves(BiTNode *root, int *num)
{
    int tmpnum = 0;
    if (num == NULL)
        return -1;

    if (root)
    {
        // 判断是否为叶子节点
        if (root->lchild == NULL && root->rchild == NULL)
        {
            (*num)++;
        }
        // 递归判断左孩子
        if (root->lchild)
        {
            CounterBiTreeLeaves(root->lchild, num);
        }
        // 递归判断右孩子
        if (root->rchild)
        {
            CounterBiTreeLeaves(root->rchild, num);
        }
    }
    else
    {
        return -1;
    }

    tmpnum = *num;
    return tmpnum;
}

二、求二叉树的深度/高度

  首先我们还是需要明确什么是树的高度。

  树的深度/高度:树中所有节点的最大层次,也称为树的高度

  求二叉树的深度/高度的操作相比而言后序遍历操作比较符合人们求二叉树的高度的方式。

    首先,分别求出左子树和右子树的高度
    然后,在左子树和右子树的高度的基础上求出输的高度。

树的高度 = MAX[ 左子树高度,右子树高度 ] + 1

  本文中的代码如下。

/**
 * 功 能:
 *      求出二叉树的深度/高度
 * 参 数:
 *      root:要操作的树
 * 返回值:
 *      树的深度/高度 
 **/
int CounterBiTreeDepth(BiTNode *root)
{
    int leftDepth = 0;
    int rightDepth = 0;
    int value = 0;

    if (root == NULL)
    {
        return value;
    }
    // 左子树的高度
    leftDepth = CounterBiTreeDepth(root->lchild);
    // 右子树的高度
    rightDepth = CounterBiTreeDepth(root->rchild);
    // 左子树的高度 或者 右子树高度 + 1
    value = 1 + (leftDepth > rightDepth ? leftDepth : rightDepth);

    return value;
}

三、拷贝二叉树

  拷贝一个二叉树,其实与创建一个二叉树的过程是一致的,也是将每一个节点一个一个的进行复制,也就是一个递归遍历创建的过程。

  首先,判断是否为空树
    1、如果是空树,则直接递归结束返回为空
    2、如果不为空树
      1)判断是否存在左子树,存在则递归复制左子树并返回节点,否则返回为空
      2)判断是否存在右子树,并且递归复制右子树并返回节点,否则返回为空
      3)申请一个新结点空间,复制当前根结点。
      4)然后将返回的左指针指向当前节点的左子树,右指针指向右子树
      5)将返回的节点的值复制给当前节点

  本文中的代码如下。

/**
 * 功 能:
 *      复制一个二叉树
 * 参 数:
 *      tree:要复制的树
 * 返回值:
 *      成功:复制之后的树的根节点
 *      失败:NULL
 **/
BiTNode *CopyBiTree(BiTNode *tree)
{
    BiTNode *newNode = NULL;
    BiTNode *newLeft = NULL;
    BiTNode *newRight = NULL;

    if (tree == NULL)
        return NULL;

    // 递归复制左子树
    if (tree->lchild != NULL)
    {
        newLeft = CopyBiTree(tree->lchild);
    }
    else
    {
        newLeft = NULL;
    }
    // 递归复制右子树
    if (tree->rchild != NULL)
    {
        newRight = CopyBiTree(tree->rchild);
    }
    else
    {
        newRight = NULL;
    }
    // malloc 根节点
    newNode = (BiTNode *)malloc(sizeof(BiTNode));
    if (newNode == NULL)
        return NULL;

    // 左指针指向左子树,右指针指向右子树
    newNode->lchild = newLeft;
    newNode->rchild = newRight;

    newNode->data = tree->data;

    return newNode;
}

四、测试案例

  前面已经说明二叉树三种比较常见的操作,均使用二叉树的递归的方法实现,对于理解二叉树的相关也能起到一定的作用吧,对于理解递归也是比较好的案例,下面就对上面三种操作方式进行验证测试。

  下面简单的编写一个测试demo,测试一下创建过程,主要的 main.c 文件中的内容如下。

#include "../src/biTree/biTree.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern func_BiTree fBiTree;

int main(int argc, const char *argv[])
{
    int ret = 0;
    printf("请按照先序输入二叉树(空用#表示):");
    BiTNode *tree = fBiTree.create();
    if (tree != NULL)
    {
        printf("二叉树创建成功!\n");
    }
    else
    {
        printf("二叉树创建出现异常!\n");
        ret = -1;
        goto ERROR_END;
    }
    printf("先序遍历输出:");
    fBiTree.prevOrder(tree);
    putchar(10);

    printf("中序遍历输出:");
    fBiTree.inOrder(tree);
    putchar(10);

    printf("后序遍历输出:");
    fBiTree.postOrder(tree);
    putchar(10);

    printf("层序遍历输出:");
    fBiTree.levelOrder(tree);
    putchar(10);

    int leaf = 0;
    fBiTree.leafNum(tree, &leaf);
    printf("树的叶子节点的个数为:%d\n", leaf);

    int depth = fBiTree.depth(tree);
    printf("树的深度/高度为:%d\n", depth);

    BiTNode *copyTree = fBiTree.copy(tree);
    printf("copy树的先序遍历输出:");
    fBiTree.prevOrder(copyTree);
    putchar(10);

    printf("copy树的中序遍历输出:");
    fBiTree.inOrder(copyTree);
    putchar(10);

    printf("copy树的后序遍历输出:");
    fBiTree.postOrder(copyTree);
    putchar(10);

    printf("copy树的层序遍历输出:");
    fBiTree.levelOrder(copyTree);
    putchar(10);

ERROR_END:
    // 释放二叉树
    fBiTree.release(tree);
    // 释放二叉树
    fBiTree.release(copyTree);

    printf("system exit with return code %d\n", ret);

    return 0;
}

  下面为工程文件的结构,使用cmake进行工程管理与编译。


$ tree biTree-cases/
biTree-cases/
├── CMakeLists.txt
├── README.md
├── image
│   └── image.jpg
├── main
│   └── main.c
├── runtime
└── src
    └── biTree
        ├── biTree.c
        └── biTree.h

5 directories, 6 files

  然后创建并编译、运行工程,详细效果如下图所示。
  
在这里插入图片描述

图4.1 测试案例的效果

  

  为了方便测试,本测试中输入的二叉树的结构如下图所示。
  
在这里插入图片描述

图4.2 测试的二叉树的结构

  

  好啦,本文就这样比较水的完成了,但是总归是在我了一段时间才算完成,总结写作不易,如果你喜欢这篇文章或者对你有用,请动动你发财的小手手帮忙点个赞,当然关注一波那就更好了,好啦,就到这儿了,么么哒(*  ̄3)(ε ̄ *)。
在这里插入图片描述

上一篇:数据结构(十三) – C语言版 – 树 - 二叉树的遍历(递归、非递归)
下一篇:数据结构(十五) – C语言版 – 树 - 二叉树的操作进阶之创建、插入、删除、查询、销毁

猜你喜欢

转载自blog.csdn.net/zhemingbuhao/article/details/105909106