玩转二叉树(之前学习了二叉树的基础知识,今天又去做了一道PAT的题目,于是就写下一篇文章进行总结)

希望各位阅读我这篇文章之后,可以对二叉树的有一个更深的认识。至于其他的数据结构知识,我会慢慢的总结的。
首先,我们就要介绍它的概念。
二叉树是一种树型结构,它的特点是每个节点至多只有两棵子树(不存在度大于2的节点),并且,二叉树的子树有左右之分,其次序不能任意颠倒。
在这里插入图片描述
二叉树的性质:
性质1:在二叉树的第i层至多有2^(i-1)个结点(i>=1)
性质2:深度为k的二叉树至多有2^k - 1个结点
性质3:对任何一棵二叉树T,如果其终端结点为n0(即叶子节点),度为2的结点数为n2,则n0=n2+1
有了这些性质,我们就可以定义一下二叉树的分类了
二叉树的分类:
满二叉树(一棵深度为k的二叉树,其结点个数为2^k-1)
完全二叉树(将一棵满二叉树的最后一层(即只有叶子结点的一层),从右到左删去n个结点,当然不能把最后一层的删完了,即n<2^(k-1),至少要给最后一层留一个叶子结点,不然就变成了深度为k-1的满二叉树了(书上的定义说得很官方,这是一个通俗的解释)。
完全二叉树也有一些特有的性质:
性质4:具有n个节点的完全二叉树的深度为log2(n)+1
还有性质5:简单来说就是将对有n个结点完全二叉树进行层次遍历,并按层序进行编号,编号为1-n。其各个结点的编号的关系。
当结点i和结点i+1在同一层时:
在这里插入图片描述
结点i和i+1不在同一层:
在这里插入图片描述
相关证明我会在之后的博客中总结。 请听下回分解。
非完全二叉树:就是一棵普通二叉树。
二叉树的存储结构:
1.可以用数组表示`

#define MAX_TREE_SIZE 100  //二叉树的最大结点数
typedef TElemType SqBiTree[MAX_TREE_SIZE];
SqBiTree bt;

注意数组是顺序存储的,因此在此约定,用一组地址连续的存储单元依次自上而下,自左至右存储完全二叉树的结点元素,将完全二叉树编号为i的结点元素存储在数组下标为i-1的分量中。当然,对于普通二叉树,结点元素可以添0占位,构造成完全二叉树,但只推荐用数组存储完全二叉树。
2.链式存储结构

typedef int ElementType;
typedef struct BiTNode
{
    ElementType date;
    struct BiTNode *left;
    struct BiTNode *right;
}BiTNode,*BiTree;

好了,讲了这么题,该让我们的题目上场了,用实战来更加深一步的学习。
L2-011 玩转二叉树(PAT)
给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。

输入格式:
输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其中序遍历序列。第三行给出其前序遍历序列。数字间以空格分隔。

输出格式:
在一行中输出该树反转后的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。
输入样例:
7
1 2 3 4 5 6 7
4 1 3 2 6 5 7
输出样例:
4 6 1 7 5 3 2

提取一下这个知识点:利用前序和中序构造二叉树,二叉树的存储和遍历。还有一些性质的考察。注意可以用前序和中序,中序和后序确定一棵二叉树,但是不能用前序和和后序确定一棵二叉树,因为前序和后序本质是差别不大,就是翻转了一下左右子树,然后这样根本就确定不了根结点,更加确定不了一棵二叉树。

现在分析一下构造方法:
第一步:从前序中找到根结点。(或者后序)
第二步:从中序找到第一步根节点在中的位置,这样位置左边的就是左子树,右边的就是左子树
第三步:递归构造左右子树

代码:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef int ElementType;
typedef struct BiTNode
{
    ElementType date;
    struct BiTNode *left;
    struct BiTNode *right;
}BiTNode,*BiTree;
BiTree createBT(int *ino,int* pre,int n)
{
    BiTree b;
    int  *p;
    int k;
    if (n<=0)
    {
        return NULL;
    }
    b = (BiTree) malloc(sizeof(BiTNode));
    b->date = *pre;//根节点
    
    for (p=ino;p<ino+n;p++)
    {
        if (*p==*pre)
        {
            break;
        }
    }
    k = p - ino;//确定根节点在中序遍历中的位置
    b->left= createBT(ino,pre+1,k);
    b->right= createBT(p+1,pre+k+1,n-k-1);
    return b;


}

这样,再翻转一下左右子树,这个比较简单,递归一下就可以了’

然后再分析一下遍历的知识
遍历有前序,中序,后序,层次遍历
其中前序,中序,后序,本质相同,只是根结点,左右结点的访问顺序不一样
而不同的是层次遍历,其本质与前面三种遍历方式不一样(方式都发生了很多变化)‘

于是这里就重点分析一下层次遍历。层次有两种方法:1.借助队列,和借助数组
1.利用队列先进先出的的特点,把每一层的结点从左至右压入队列,再遍历输出,再把已经遍历的结点出队。

扫描二维码关注公众号,回复: 11132417 查看本文章

在题目中我使用的利用数组进行层次遍历。
2.用数组实现:
利用双指针法,两个指针,inside和out。
第一步:先把根节点存入数组下标为0的分量,并把inside更新。
第二步:输出数组下标为0的元素,将根节点的左右孩子存入数组下标1和2中
第三步:注意在后面更新out,让out值加一。out值就指向了每一层的结点。out落后于inside。然后不断更新数组下标和数组下标里的分量,进行循环即可

在题目中,因为我是用的c语言写,就用了数组来进行层次遍历,但是确实利用队列会更加好理解

1.利用队列(如果用c语言写,还要写一下队列的代码)

void LevelTraversal(BiTree T)//层次遍历用队列实现
{
    Init(&q);
    if (Tree!=NULL)
    {
        EnQueue(&q,T);
     }
    while (IsEmpty()==FALSE)
    {
         printf("%d",q.front->date);
         if (q.front->left!=NULL)
         {
              EnQueue(&q,q.front->left);
          }
          if (q.front->right!=NULL)
         {
             EnQueue(&q,q.front->right);
         }
         DeQueue(&q);//这里要传入二级指针,这里才能改变一级指针q的指向。   
}

队列的其他知识也会在不知道多少回中进行分解,哈哈哈。

下面就给出题目的代码,代码里有利用数组的层次遍历

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef int ElementType;
typedef struct BiTNode
{
    ElementType date;
    struct BiTNode *left;
    struct BiTNode *right;
}BiTNode,*BiTree;
BiTree createBT(int *ino,int* pre,int n)
{
    BiTree b;
    int  *p;
    int k;
    if (n<=0)
    {
        return NULL;
    }
    b = (BiTree) malloc(sizeof(BiTNode));
    b->date = *pre;//根节点
    
    for (p=ino;p<ino+n;p++)
    {
        if (*p==*pre)
        {
            break;
        }
    }
    k = p - ino;//确定根节点在中序遍历中的位置
    b->left= createBT(ino,pre+1,k);
    b->right= createBT(p+1,pre+k+1,n-k-1);
    return b;


}
BiTree Reverse(BiTree T)
{
    if (T==NULL)
    {
        return NULL;
    }
    BiTree B;
    B =(BiTree) malloc(sizeof(BiTNode));
    B = T->left;
    T->left = T->right;
    T->right = B;
    Reverse(T->left);
    Reverse(T->right);
    return T;
}
void LevelTraversal(BiTree T)//层次遍历用数组实现。
{
    int inside = 0;
    int out = 0;
    BiTree temp[101];//数组指针(数组里存的都是指针),来存各个结点。
    temp[inside++] = T;
    while (inside>out)
    {
        if (temp[out])
        {
            if(out!=0)
            {
                printf(" ");
            }
            printf("%d",temp[out]->date);
            temp[inside++] = temp[out]->left;//感觉也是模拟了队列。
            temp[inside++] = temp[out]->right;
        }
        out++;
    }


   
    
}
int main()
{
    int N;
    scanf("%d",&N);
    int ino[30];
    int pre[30];
    BiTree T = NULL;
    for (int i=0;i<N;i++)
    {
        scanf("%d",&ino[i]);
    }
    for (int i=0;i<N;i++)
    {
        scanf("%d",&pre[i]);
    }
    T=createBT(ino,pre,N);
    Reverse(T);
    LevelTraversal(T);


}

今天的总结和做题就到这,拜拜啦,至于其他的遍历,就是递归。大家加油
如果有什么错误,欢迎大佬指出。

在这里插入图片描述

加油啊,不要放弃。

发布了16 篇原创文章 · 获赞 12 · 访问量 1476

猜你喜欢

转载自blog.csdn.net/weixin_45290352/article/details/105649220
今日推荐