0


二叉树的遍历+基础练习

前面完全二叉树适合存放数据,又因为它在内存中连续存储,因此用顺序表来实现它,并介绍了堆排序及TOP-K问题。

今天我们了解一下二叉树的遍历问题,并完成几道二叉树基础练习



二叉树的遍历

二叉树的遍历有:前序/中序/后序的递归结构遍历:

1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之间。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

以下面二叉树为例,进行先序,中序,后序遍历:

先序

分析:从根节点开始,先访问根,再访问左子树(左子树中先访问根节点,在访问左子树和右子 树),最后访问右子树(先访问根节点,在访问左子树和右子树)

访问顺序:

先访问树tree根1,再访问tree左子树L1:

访问L1根2,再访问其左子树Ll2:

访问Ll2根3,再访问其左子树:左子树为空,访问其右子树,右子树为空,返回上一个子树L1;

此时L1左子树访问完毕,访问L1右子树NULL,为空返回上一个树tree;

此时tree根和左子树访问完毕,访问tree右子树R1:

访问R1根4,再访问R1左子树Rl1:

访问Rl1根5,再访问Rl1左子树和右子树NULL,返回上一个树R1;

此时,R1左子树Rl1访问完毕,接着访问R1右子树Rr1:

访问Rr1根6,再访问Rr1左右子树NULL,返回上一个树R1;

此时R1根和左右子树访问完毕,返回上一个树tree;

此时tree的根和左子树访问完毕,及整个树访问完毕。

图示:

中序

分析:即先访问左子树,左子树访问完毕后再访问根节点,根节点访问完后,最后访问右子树。左子树和右子树中也是先访问左子树,再根,最后右子树

访问顺序:

从tree根开始,先访问其左子树L1:

左子树L1不为空,访问L1左子树Ll2:

左子树Ll2不为空,访问Ll2左子树:

左子树为空,访问Ll2根3,再访问Ll2的右节点,右节点为空,返回子树L1;

子树L1的左子树访问完毕,访问L1的根2,再访问L1的右子树,为空,返回树tree;

tree的左子树访问完毕,访问tree根1,接着访问tree右子树R1:

右子树R1不为空,访问R1左子树Rl1:

Rl1不为空,访问Rl1左子树,左子树为空,访问Rl1根5,再访问其右子树:

右子树为空,返回上一个树R1;

R1左子树访问完毕,访问其根4,接着访问其右子树Rr1:

Rr1不为空,访问其左子树,左子树为空,访问Rr1根6,再访问其右子树为空,返回R1;

此时tree的左子树,根,和右子树都访问完毕。

图示:

后序

分析:先访问左子树(左子树中也是左子树,右子树,根),再访问右子树(右子树中也是左子树,右子树,根),最后访问根节点。

访问顺序:

先访问tree,不为空,访问其左子树L1,L1不为空,访问其左子树Ll2;

Ll2不为空,访问其左子树,为空;访问其右子树,为空;访问其根3,返回上一个树L1;

L1左子树访问完毕,访问其右子树,为空;访问其根2,返回上一个树tree;

tree的左子树访问完毕,访问其右子树R1,R1不为空,访问其左子树Rl1;

Rl1不为空,访问其左子树,为空;访问其右子树,为空,访问其根5,返回上一个树R1;

R1左子树访问完毕,访问其右子树Rr1,Rr1不为空,访问其左子树;

Rr1左子树为空,访问其右子树,为空,访问其根6,返回上一个树R1;

此时R1左子树右子树访问完毕,访问其根4,放回上一个树tree;

此时tree的左右子树访问完毕,访问其根1;整个树访问完毕。

图示:

手动构建链式二叉树

不难发现,上述遍历时中途总会返回上一层树,已经用到了递归思想,这里我们手动实现一个简单的链式二叉树,完成我们的前序,中序,后序的遍历。

定义

定义每个节点由数据,左子树地址和右子树地址组成

typedef int BTDataType;
typedef struct BinaryTreeNode
{
    BTDataType data;  //数据
    struct BinaryTreeNode* left;   //左子树地址
    struct BinaryTreeNode* right;  //右子树地址
}BTNode;

创建节点

将固定的数据放入创建的节点中,左右子树指针置空

BTNode* BuyNode(BTDataType x)
{
    BTNode* root = (BTNode*)malloc(sizeof(BTNode));
    root->data = x;
    root->left = NULL;
    root->right = NULL;
    return root;
}

创建二叉树

手动创建节点,并将左右子树指针指向固定的位置,以上述二叉树为例:

//手动创建
BTNode* CreatBinaryTree()
{
    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 node1;
}

前序遍历

依照我们对前序遍历的顺序分析:根,左子树,右子树,编写前序代码:

// 二叉树前序遍历
void PreOrder(BTNode* root)
{
    if (root==NULL)
    {
        return;
    }
    printf("%d ",root->data);
    PreOrder(root->left);
    PreOrder(root->right);
}

中序遍历

// 二叉树中序遍历
void InOrder(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }
    InOrder(root->left);
    printf("%d ", root->data);
    InOrder(root->right);
}

后序遍历

// 二叉树后序遍历
void PostOrder(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }
    PostOrder(root->left);
    PostOrder(root->right);
    printf("%d ", root->data);
}

练习

求二叉树节点树

方法一:前序中序后序时添加一个计数变量(变量为全局或者静态,防止递归时计数重置)

缺点:重复调用时计数会累加,每次调用时须将count重新置0;

//定义全局或者静态变量
//多次调用会累加
int count = 0;
int BTreeSize(BTNode* root)
{
    if (root == NULL)
        return;
    ++count;
    BTreeSize(root->left);
    BTreeSize(root->right);
    return  count;
}

方法二:遍历+计数(遍历时将遍历地址传过去)

//遍历+计数
//将变量地址传过去,计数---思想最优
void BTreeSize(BTNode* root,int* count)
{
    if (root == NULL)
        return;
    ++(*count);
    BTreeSize(root->left,count);
    BTreeSize(root->right,count);
}

方法三:递归--分治思想

当根为空时,返回0,左子树节点个数+右子树节点个数+1(根节点本身)

//递归--分治思想--节点个数
int BTreeSize(BTNode* root)
{
    return root == NULL ? 0 : BTreeSize(root->left) + BTreeSize(root->right) + 1;
}

求二叉树叶子节点个数

二叉树叶子节点数 = 左子树叶子节点数 + 右子树节点数

叶子节点:左子树和右子树为空的节点

//叶子节点个数
int BTreeLeafSize(BTNode* root)
{
    if (root==NULL)
    {
        return 0;
    }
    if (root->left == NULL&&root->right==NULL)
    {
        return 1;
    }
    return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}

第k层节点数

求tree的第三层节点数

即L1和R1的第二层节点数之和

即Ll1、Rl1和Rr1第一层节点数之和

当k=1时,返回1即可

//第k层节点个数
int BTreeLeveSize(BTNode* root,int k)
{
    assert(k>=1);
    if (root==NULL)
    {
        return 0;
    }
    if (k == 1)
    {
        return 1;
    }
    return BTreeLeveSize(root->left, k - 1) + BTreeLeveSize(root->right, k - 1);

二叉树深度

二叉树的深度 = 左子树和右子树中最大深度度+1

需要比较左右子树高度,判断返回哪一个

//二叉树深度
int BTreeDepth(BTNode* root)
{
    if (root==NULL)
    {
        return 0;
    }
    int leftdepth = BTreeDepth(root->left);
    int rightdepth = BTreeDepth(root->right);
    return leftdepth >rightdepth ? leftdepth + 1 : rightdepth + 1;
}

二叉树查找值为x的节点

判断根是否为要找的节点,是则返回节点地址

不是则进左子树中去找,找到返回节点地址,找不到返回空

再进右子树取找,找到返回节点地址,找不到返回空

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
    if (root==NULL)
    {
        return NULL;
    }
    if (root->data == x)
        return root;
    if (BinaryTreeFind(root->left, x))
        return BinaryTreeFind(root->left,x);
    if (BinaryTreeFind(root->right, x))
        return BinaryTreeFind(root->right, x);
    return NULL;
}

标签: 链表 c语言 算法

本文转载自: https://blog.csdn.net/weixin_53316121/article/details/124155945
版权归原作者 i跑跑 所有, 如有侵权,请联系我们删除。

“二叉树的遍历+基础练习”的评论:

还没有评论