二叉树

二叉树

1.树的定义与基本术语

树是一类重要的非线性数据结构,是以分支关系定义的层次结构。

1.1.树的定义

树(tree)是n(n≥ 0)个结点的有限集T,当n=0时,称为空树。当n>0时,该集合满足如下条件:有且仅有一个特定的结点,称为树的根(root),它没有直接前驱,但有零个或多个直接后继。其余结点可分为m(m ≥ 0)个互不相交的有限集T1,T2,T3,…,Tm ,其中每一个集合Ti本身又是一棵树,称为根的子树(subtree)。每棵子树的根结点有且仅有一个直接前驱,但有零个或多个直接后继。

注意:树的定义具有递归性,即树中还有树。

1.2.树的常用术语

  1. 结点:包含一个数据元素及若干指向其它结点的分支信息。

  2. 结点的度:一个结点的子树个数称为此结点的度。

  3. 叶结点:度为0的结点,即无后继的结点,也称为终端结点。

  4. 分支结点:度不为0的结点,也称为非终端结点。

  5. 结点的层次:从根结点开始定义,根结点的层次为1,根的直接后继的层次为2,依此类推。

  6. 孩子结点:一个结点的直接后继称为该结点的孩子结点。或:结点的子树的根称为该结点的孩子。

  7. 双亲结点:一个结点的直接前驱称为该结点的双亲结点。

  8. 兄弟结点:同一双亲结点的孩子结点之间互称兄弟结点。

  9. 堂兄弟:双亲在同一层的结点称为堂兄弟结点。

  10. 祖先结点:一个结点的祖先结点是指从根结点到该结点的路径上的所有结点。

  11. 子孙结点:一个结点的直接后继和间接后继称为该结点的子孙结点。或:以某结点为根的子树中的任一结点都称为该结点的子孙。

  12. 前辈:层号比该结点小的结点都称为该结点的前辈。

  13. 后辈:层号比该结点大的结点都称为该结点的后辈。

  14. 树的度:树中所有结点的度的最大值。

  15. 结点的层序编号:将树中结点按从上层到下层、同层从左到右的次序排成一个线性序列,依次给它们编以连续的自然数。

  16. 树的高度(深度):树中所有结点的层次的最大值。

  17. 有序树:在树T中,如果各子树Ti之间是有先后次序的,则称为有序树。

  18. 森林:m(m≥0)棵互不相交的树的集合。将一棵非空树的根结点删去,树就变成一个森林;反之,给森林增加一个统一的根结点,森林就变成一棵树。

  19. 同构:对两棵树,通过对结点适当地重命名,就可以使两棵树完全相等(结点对应相等,对应结点相关关系也相等),则称这两棵树同构。

2.二叉树

2.1.二叉树的定义

定义:满足以下两个条件的树结构叫做二叉树(Binary Tree):

  • 每个结点的度都不大于2;

  • 每个结点的孩子结点次序不能任意颠倒。

特点:

  1. 每个结点至多有二棵子树(孩子)(即不存在度大于2的结点)

  2. 二叉树的子树(孩子)有左、右之分,且其次序不能任意颠倒

2.2.二叉树的性质

  • 性质一:在二叉树的第i层上至多有2i-1个结点(i≥1)

  • 性质二:深度为k的二叉树至多有2k-1个结点(k≥1)

  • 性质三:对任意一棵二叉树T,若终端结点数为n0,而其度数为2的结点数为n2,则n0=n2+1

  • 性质四:具有n个结点的完全二叉树的深度为㏒2n+1。

  • 性质五: 对于具有n个结点的完全二叉树,如果按照从上到下和从左到右的顺序对二叉树中的所有结点从1开始顺序编号,则对于任意的序号为i的结点有:

  1. 如i=1,则序号为i的结点是根结点,无双亲结点;如i>1,则序号为i的结点的双亲结点序号为 i/2 。

  2. 如2×i>n,则序号为i的结点无左孩子(叶子结点);如2×i≤n,则序号为i的结点的左孩子结点的序号为2×i。

  3. 如2×i+1>n,则序号为i的结点无右孩子;如2×i+1≤n,则序号为i的结点的右孩子结点的序号为2×i+1。

2.3.构建一个二叉树

2.3.1.数据结构

typedef struct BiTNode 
{
    BiElemType data;//数据
    struct BiTNode* Lchild;
    struct BiTNode* Rchild;
}BiTNode,*BiTree;

2.3.2.初始化二叉树

  1. 利用辅助链表,指向父结点

    辅助链表的结构:

    typedef struct tag {
        BiTree p;//存的是树的结点地址
        struct tag* pnext;
    }tag_t, * ptag_t;
  2. 利用循环读取创建树的结点

    int main() 
    {
        BiTree pnew;
        char c;
        BiTree tree = NULL;
        ptag_t ptail = NULL, listpnew=NULL, pcur=NULL;
        while (scanf("%c", &c) != EOF)
        {
            if (c == '\n')
            {
                break;
            }
            pnew = (BiTree)calloc(1, sizeof(BiTNode));//开辟空间
            pnew->data = c;//将数据放进去
            listpnew = (ptag_t)calloc(1, sizeof(tag_t));
            listpnew->p = pnew;
            if (NULL == tree)
            {
                tree = pnew;//树的根
                ptail = listpnew;//链表尾
                pcur = listpnew;//pcur 指向父结点
                continue;
            }
            else
            {
                ptail->pnext = listpnew;//新结点放入链表,通过尾插法
                ptail = listpnew;//ptail重新指向链表尾部
            }
            if (NULL == pcur->p->Lchild) //如果左孩子为空
            {
                pcur->p->Lchild = pnew;//把新结点放到要父结点的左孩子
            }
            else if (NULL == pcur->p->Rchild) //左孩子不为空 且右孩子为空
            {
                pcur->p->Rchild = pnew;//将新结点放在父结点的右孩子
                pcur = pcur->pnext;//让父节点 移动到链表的下一个
            }
        }
    }

2.4.遍历

二叉树的遍历:指按一定规律 对二叉树的每个结点进行访问且仅访问一次

先序、中序、后序遍历是递归定义的,即在其子树中亦按上述规律进行遍历。

2.4.1.先序遍历

先访问根,再遍历左子树,最后遍历右子树

算法设计:

void preOrder(BiTree p) 
{
    if (p != NULL) 
    {
        putchar(p->data);
        preOrder(p->Lchild);
        preOrder(p->Rchild);
    }
}

2.4.2.中序遍历

先遍历左子树,再访问根,最后遍历右子树

算法设计:

void InOrder(BiTree p) 
{
    if (p != NULL) 
    {
        InOrder(p->Lchild);
        putchar(p->data);
        InOrder(p->Rchild);
    }
​
}

2.4.3.后序遍历

先遍历左子树,再遍历右子树,最后访问根

算法设计:

void PostOrder(BiTree p) 
{
    if(p!=NULL)
    {
        PostOrder(p->Lchild);
        PostOrder(p->Rchild);
        putchar(p->data);
    }
}

 

 

 

 

 

 

posted @ 2022-10-20 21:40  骆宾王  阅读(82)  评论(0)    收藏  举报