普通链式二叉树(1)

普通链式二叉树(1)

前面我们已经讲了用数组来表示二叉树的方式和功能。这节课,我们就来讲讲普通的链式二叉树。

啥叫链式二叉树呢,其实很简单,就像链表那样,通过指针标记左右孩子节点。结构如下图:

在这里插入图片描述

遍历顺序(方式)

那么对于链式二叉树呢,我们首先要学习的就是它的几种遍历方式:前序遍历,中序遍历,后序遍历,后续还有一个层序遍历。我们先讲一下前面3种。

在这里插入图片描述
在这里插入图片描述

前序遍历

首先我这里讲的前中后,到底是谁的前中后呢?答案是:二叉树的根节点的前中后序。就以前序遍历来说,前序遍历也可以说是先根遍历。也就是先遍历根节点,然后遍历左子树,最后遍历右子树。这里大家可以发现,我没有说遍历左孩子,右孩子。

那么这里就涉及到了链式二叉树的一个重要思路:递归遍历。在一开始介绍树的时候,我提到了(树的递归定义)

在这里插入图片描述

大家通过上图,应该是可以理解树和子树的概念了。那么现在我用一颗简单的链式二叉树使用一次前序遍历给大家看。(因为无法视频演示,希望大家可以看懂)

在这里插入图片描述

中序遍历

和前序没什么区别,其实这三种遍历没有本质区别,就是遍历根节点的先后问题。是先遍历根节点还是中间遍历根节点,还是最后遍历根节点。

在这里插入图片描述

后序遍历

在这里插入图片描述

作用

那么普通链式二叉树有啥作用呢?目前来看是没啥作用哈。你看,之前我们学习顺序表昂,链表,堆这些都要实现增,删,查,改,这4个功能。那么我们学习普通链式二叉树要实现这些吗?不用哈。

首先呢,普通链式二叉树能做到的,链表这些简单数据结构也可以做到,所以普通链式二叉树就没必要多插一脚。而且对于普通链式二叉树来说,插入数据,插入哪里呢?删除数据,删除哪一个呢?这样做又有什么用呢?

所以说,普通的链式二叉树没啥用处。但是,为什么我们还要学呢?因为这是为了后面的高阶数据结构,那种听着就很nb的那种数据结构做铺垫,比如二叉搜索树,AVL树,红黑树等等。其次就是,考试会出题,面试可能也会出题,也就是应付考试。

手工建树

为了更好的学习后面的知识呢,我们得人工的建一颗链式二叉树出来。为什么说人工呢,因为这就是一个一个节点创建出来,然后链接起来。这并不是真正的「创建链式二叉树」,意思就是说这并不是最权威的建树方法。后面我们再着重讲怎么建树比较权威。

#include<stdio.h>
#include<stdlib.h>
#include <assert.h>

typedef struct BinaryTreeNode
{
    struct BinaryTreeNode* left;
    struct BinaryTreeNode* right;
    int val;
}BTNode;

BTNode* BuyNode(int x)
{
    BTNode* node = (BTNode*)malloc(sizeof(BTNode));
    if (node == NULL)
    {
        perror("malloc fail");
        exit(-1);
    }

    node->val = x;
    node->left = NULL;
    node->right = NULL;

    return node;
}
int main()
{
    // 手动构建
    BTNode* node1 = BuyNode(1);
    BTNode* node2 = BuyNode(2);
    BTNode* node3 = BuyNode(3);
    BTNode* node4 = BuyNode(4);
    BTNode* node5 = BuyNode(5);
    BTNode* node6 = BuyNode(6);

    node1->left = node2;
    node1->right = node4;
    node2->left = node3;
    node4->left = node5;
    node4->right = node6;

    return 0;
}

在这里插入图片描述

递归实现顺序遍历

完整测试代码

#include<stdio.h>
#include<stdlib.h>
#include <assert.h>

typedef struct BinaryTreeNode
{
    struct BinaryTreeNode* left;
    struct BinaryTreeNode* right;
    int val;
}BTNode;

BTNode* BuyNode(int x)
{
    BTNode* node = (BTNode*)malloc(sizeof(BTNode));
    if (node == NULL)
    {
        perror("malloc fail");
        exit(-1);
    }

    node->val = x;
    node->left = NULL;
    node->right = NULL;

    return node;
}
void PrevOrder(BTNode* root) {
    if (root == NULL) {
        printf("NULL ");

        return;
    }

    printf("%d ", root->val);
    PrevOrder(root->left);
    PrevOrder(root->right);
}
void InOrder(BTNode* root)
{
    if (root == NULL)
    {
        printf("NULL ");
        return;
    }

    InOrder(root->left);
    printf("%d ", root->val);
    InOrder(root->right);
}
void PostOrder(BTNode* root)
{
    if (root == NULL)
    {
        printf("NULL ");
        return;
    }

    PostOrder(root->left);
    PostOrder(root->right);
    printf("%d ", root->val);
}
int main()
{
    // 手动构建
    BTNode* node1 = BuyNode(1);
    BTNode* node2 = BuyNode(2);
    BTNode* node3 = BuyNode(3);
    BTNode* node4 = BuyNode(4);
    BTNode* node5 = BuyNode(5);
    BTNode* node6 = BuyNode(6);

    node1->left = node2;
    node1->right = node4;
    node2->left = node3;
    node4->left = node5;
    node4->right = node6;

    PrevOrder(node1);
    printf(("\n"));
    InOrder(node1);
    printf("\n");
    PostOrder(node1);

    return 0;
}

依旧是前中后序遍历,现在我们就要写代码实现前中后序遍历。

  • 前序遍历
void PrevOrder(BTNode* root) {
    if (root == NULL) {
        printf("NULL ");

        return;
    }

    printf("%d ", root->val);
    PrevOrder(root->left);
    PrevOrder(root->right);
}

在这里插入图片描述

在这里插入图片描述

由于没办法在你身边演示给你看,所以大家结合上面前序遍历的图解,还有自己调试一下代码,看一下代码运行的过程,结合递归的知识,我想应该是可以明白的。

在这里插入图片描述

  • 中序遍历
void InOrder(BTNode* root)
{
    if (root == NULL)
    {
        printf("NULL ");
        return;
    }

    InOrder(root->left);
    printf("%d ", root->val);
    InOrder(root->right);
}

在这里插入图片描述
在这里插入图片描述

  • 后序遍历
void PostOrder(BTNode* root)
{
    if (root == NULL)
    {
        printf("NULL ");
        return;
    }

    PostOrder(root->left);
    PostOrder(root->right);
    printf("%d ", root->val);
}

在这里插入图片描述

递归实现分治

后面是会有几个小问题,都需要用到分治的思想解决。

那么分治是啥意思呢?很简单,分开统治。这就像公司各级领导,一级领导统领二级领导,二级领导接着统领三级领导,以此类推。在学校,校长统领各院院长,院长统领系领导,系领导又统领各普通老师/辅导员,老师又统领各班班长,班长负责管理班级学生。这就是分而治之,也就是分治。

那么这样分级的意义是啥呢?这么说吧,假设教育局叫校长统计一下到校人数,难道校长要亲自到每个班级,每个宿舍里,点名,然后点到一个记录一个吗?那当然不会,这样校长哪里还有时间去找小美昂?对吧

很简单,校长叫院长办事,院长叫系主任办事,系主任叫老师办事,老师叫班长办事,所以一个大问题(统计全部到校人数)就变成了一个小问题(计算每个班的人数),最后层层往上上报想加就搞定了。可以参考下图:

在这里插入图片描述

了解分治大概思想之后就开始实践一下吧

全部测试代码:

#include<stdio.h>
#include<stdlib.h>
#include <assert.h>

typedef struct BinaryTreeNode
{
    struct BinaryTreeNode* left;
    struct BinaryTreeNode* right;
    int val;
}BTNode;

BTNode* BuyNode(int x)
{
    BTNode* node = (BTNode*)malloc(sizeof(BTNode));
    if (node == NULL)
    {
        perror("malloc fail");
        exit(-1);
    }

    node->val = x;
    node->left = NULL;
    node->right = NULL;

    return node;
}
void PrevOrder(BTNode* root) {
    if (root == NULL) {
        printf("NULL ");

        return;
    }

    printf("%d ", root->val);
    PrevOrder(root->left);
    PrevOrder(root->right);
}
void InOrder(BTNode* root)
{
    if (root == NULL)
    {
        printf("NULL ");
        return;
    }

    InOrder(root->left);
    printf("%d ", root->val);
    InOrder(root->right);
}
void PostOrder(BTNode* root)
{
    if (root == NULL)
    {
        printf("NULL ");
        return;
    }

    PostOrder(root->left);
    PostOrder(root->right);
    printf("%d ", root->val);
}
int TreeSize(BTNode* root)
{
    return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

// 叶子节点个数
int TreeLeafSize(BTNode* root)
{
    if (root == NULL)
        return 0;

    if (root->left == NULL && root->right == NULL)
    {
        return 1;
    }

    return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

// 第k层的节点个数
int TreeKLevel(BTNode* root, int k)
{
    assert(k > 0);

    if (root == NULL)
        return 0;

    if (k == 1)
    {
        return 1;
    }

    return TreeKLevel(root->left, k - 1)
           + TreeKLevel(root->right, k - 1);
}
int main()
{
    // 手动构建
    BTNode* node1 = BuyNode(1);
    BTNode* node2 = BuyNode(2);
    BTNode* node3 = BuyNode(3);
    BTNode* node4 = BuyNode(4);
    BTNode* node5 = BuyNode(5);
    BTNode* node6 = BuyNode(6);

    node1->left = node2;
    node1->right = node4;
    node2->left = node3;
    node4->left = node5;
    node4->right = node6;

    PrevOrder(node1);
    printf(("\n"));
    InOrder(node1);
    printf("\n");
    PostOrder(node1);
    printf("\n");

    printf("%d\n", TreeSize(node1));

    return 0;
}

大家调用函数自己去试就好了

  • 计算一棵树的节点个数
int TreeSize(BTNode* root)
{
    return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 计算一棵树的叶子节点的个数
// 叶子节点个数
int TreeLeafSize(BTNode* root)
{
    if (root == NULL)
        return 0;

    if (root->left == NULL && root->right == NULL)
    {
        return 1;
    }

    return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

在这里插入图片描述
在这里插入图片描述

  • 计算一棵树第k层节点个数
// 第k层的节点个数
int TreeKLevel(BTNode* root, int k)
{
    assert(k > 0);

    if (root == NULL)
        return 0;

    if (k == 1)
    {
        return 1;
    }

    return TreeKLevel(root->left, k - 1)
           + TreeKLevel(root->right, k - 1);
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值