初阶数据结构学习记录——열하나 层序和二叉树的创建与销毁

目录

一、二叉树遍历(牛客网)

二、判断是否为完全二叉树

 层序遍历

三、二叉树的销毁


补充上一篇笔记

一、二叉树遍历(牛客网)

编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。

输入描述:

输入包括1行字符串,长度不超过100。

输出描述:

可能有多组测试数据,对于每组数据, 输出将输入字符串建立二叉树后中序遍历的序列,每个字符后面都有一个空格。 每个输出结果占一行。

二叉树遍历_牛客题霸_牛客网 (nowcoder.com)

按照题中给的例子可以画出整个树的图。字符串是前序遍历的结果,题目让我们最终打印中序结果。思路能想到,把原本的树建立出来然后中序遍历即可,所以重点在于如何建立。

#代表NULL,也就是没有节点。所以说遇到#就return NULL。如果不是#,那就需要建立一个指针,开辟一块空间,然后给值。接下去,如何递归?我们肯定还要判断是不是空,每次也要建立一个指针,递归过程不能丢掉任何一个指针,所以最一开始就定义左右指针,来接受递归后形成的树。当然还有一个用来走下标的i。

TreeS* rebuildTree(char* str, int* i)
{
    if (str[*i] == '#')
    {
        (*i)++;
        return NULL;
    }
    TreeS* root = (TreeS*)malloc(sizeof(TreeS));
    root->val = str[(*i)++];
    root->left = rebuildTree(str, i);
    root->right = rebuildTree(str, i);
    return root;
}

为何要用指针i?如果是一个只有两层的二叉树,1 2 3节点,1访问完后,来到2,2没有左右子树,访问完2后,i应当指向下一个数字3,但是如果是传值调用,那么访问2的左子树时,遇到空,i++,返回NULL,这时候的i不会影响到访问2节点的函数里的i,下一行的访问右子树时传入的i还是原先的i,还是在访问2的左子树的空,等到2这里的程序结束后,本应当访问3,但此时 str[i] 并不是3,而是空,就会导致程序出错,所以应当传址调用,这点画递归展开的图就能够理解。

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

typedef struct TreeString
{
    char val;
    struct TreeString* left;
    struct TreeString* right;
}TreeS;

TreeS* rebuildTree(char* str, int* i)
{
    if (str[*i] == '#')
    {
        (*i)++;
        return NULL;
    }
    TreeS* root = (TreeS*)malloc(sizeof(TreeS));
    root->val = str[(*i)++];
    root->left = rebuildTree(str, i);
    root->right = rebuildTree(str, i);
    return root;
}

void Inorder(TreeS* root)
{
    if (root == NULL)
    {
        return;
    }
    Inorder(root->left);
    printf("%c ", root->val);
    Inorder(root->right);
}

int main()
{
    char str[100];
    scanf("%s", str);
    int i = 0;
    TreeS* root = rebuildTree(str, &i);
    Inorder(root);
    return 0;
}

二、判断是否为完全二叉树

 层序遍历

层序遍历是要用到队列的。取到根节点,插入,如果队列不为空,那么就拿出1,放入2和4,队列不为空,那就拿出2,插入3,此时队列有4和3,循环一遍后拿出4,插入5和6,一步步往下,层序就打印出来了。 也就是说,每一层不是一起,全部都放进去,随着上一层的数据拿走一个,就插入进它的左右子树,上一层的数据都拿出来后,下一层的数据也都放进去了。

void LevelOrder(BTNode* root)
{
    Queue q;
    QueueInit(&q);
    if (root)
        QueuePush(&q, root);

    while (!QueueEmpty(&q))
    {
        BTNode* front = QueueFront(&q);
        printf("%d ", front->data);
        QueuePop(&q);

        if (front->left)
        {
            QueuePush(&q, front->left);
        }

        if (front->right)
        {
            QueuePush(&q, front->right);
        }
    }
    printf("\n");

    QueueDestroy(&q);
}

解决层序问题再来看如何判断完全二叉树。完全二叉树的最后一层是从左面开始有连续的节点,上一层挨着的两个节点应当是都有左右子树,或者左面那个有2个子树,而右面那个只有左子树,这就是连续的,但如果右面那个只有右子树,就不连续,就不是完全二叉树。如果最后一层全是节点,那是满二叉树,不是完全二叉树。

完全二叉树会在最后一层有变化,也就是会遇到空,遇到空还要考虑连续问题,从空开始,之后的每个节点都是空,那么就是完全二叉树,反之则不是。利用层序遍历,每次拿走队顶元素后,判断是否为空,为空就去判断后面的节点。在之前的层序遍历中可以发现,每到新的一层,这一层的所有节点都已经进入了队列中,所以只需要遍历一遍看看是否为空即可。

bool TreeComplete(BTNode* root)
{
    Queue q;
    QueueInit(&q);
    if (root)
        QueuePush(&q, (*root));
    while (!QueueEmpty(&q))
    {
        BTNode* front = QueueFront(&q);
        QueuePop(&q);
        if (front == NULL)
        {
            break;//只要碰到空,那就出来循环,开始判断,因为前面不可能为空,有空就说明是在最后一层
        }
        else
        {
            //不判断是不是空,直接插入
            QueuePush(&q, front->left);
            QueuePush(&q, front->right);
        }
    }
    while (!QueueEmpty(&q))//假如是完全二叉树,虽然后面插入的都是空,但队列也不是空的,所以会进入循环
    {
        BTNode* front = QueueFront(&q);
        QueuePop(&q);
        if (front != NULL)//碰到第一个空出来后,再次取队顶,就是第一个空后面的那个元素,所以就可以开始判断了
        {
            QueueDestroy(&q);
            return false;
        }
    }
    QueueDestroy(&q);
    return true;
}

三、二叉树的销毁

最优解是用后序遍历。因为要防止根部被先砍掉。

void TreeDestroy(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }
    TreeDestroy(root->left);
    TreeDestroy(root->right);
    free(root);
}

结束,下一篇写排序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值