二叉树

满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。

 

完全二叉树:对一棵具有n个结点的二叉树按层序编号,如果编号为i(1≤i≤n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树

 

 二叉树的特点:每个结点最多有两棵子树,左子树和右子树是有顺序的,次序不能任意颠倒.

二叉树性质:

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

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

  性质3:对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1.

  性质4:具有几个结点的完全二叉树的深度为| log2n+1 | (|x|表示不大于x的最大整数).

  性质5:如果对一棵有n个结点的完全二叉树的结点按层序编号(从第1层到第层,每层从左到右),对任一结点i (1≤i≤n)有: 1.如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点.2.如果2i>n, 则结点i无左孩子(结点i为叶子结点) ; 否则其左孩子是结点2i.3.如果2i+1>n, 则结点i无右孩子;否则其右孩子是结点2i+1.

 

构造二叉树

   如果我们要在内存中建立一个如图6-9-1左图这样的树,为了能让每个结点确认是否有左右孩子,我们对它进行了扩展,变成图6-9-1右图的样子,也就是将二叉树中每个结点的空,指针引出一个虚结点,其值为一特定值,比如“#”。我们称这种处理后的二叉树为原二叉树的扩展二叉树。扩展二叉树就可以做到一个遍历序列确定一棵二叉树了。比如图6-9-1的前序遍历序列就为AB#D##C##。

 

                图6-9-1

/* 按前序输入二叉树中结点的值(一个字符) */
/* #表示空树,构造二叉链表表示二叉树T。 */
void CreateBiTree(BiTree *T)
{
    TElemType ch;
    scanf("%c", &ch);
    if (ch == '#')
        *T = NULL;
    else
    {
        *T = (BiTree)malloc(sizeof(BiTNode));    /* 生成根结点 */
        if (!*T)
            exit(OVERFLOW);
        (*T)->data = ch;
        CreateBiTree(&(*T)->lchild);    /* 构造左子树 */
        CreateBiTree(&(*T)->rchild);    /* 构造右子树 */
    }
}

 

线索二叉树

 

    通过考察各种二叉链表,不管儿叉树的形态如何,空链域的个数总是多过非空链域的个数。准确的说,对于一个有n个结点的二叉链表,每个结点有指向左右孩子的两个指针域,所以一共是2n个指针域。而n个结点的二叉树一共有n-1条分支线数,也就是说,其实是存在2n-(n-1)=n+1个空指针域;如下图所示。

 

 

因此,提出了一种方法,利用原来的空链域存放指针,指向树中其他结点。这种指针称为线索。

    记ptr指向二叉链表中的一个结点,以下是建立线索的规则:

    (1)如果ptr->lchild为空,则存放指向中序遍历序列中该结点的前驱结点。这个结点称为ptr的中序前驱;

    (2)如果ptr->rchild为空,则存放指向中序遍历序列中该结点的后继结点。这个结点称为ptr的中序后继;

    显然,在决定lchild是指向左孩子还是前驱,rchild是指向右孩子还是后继,需要一个区分标志的。因此,我们在每个结点再增设两个标志域ltag和rtag,注意ltag和rtag只是区分0或1数字的布尔型变量,其占用内存空间要小于像lchild和rchild的指针变量。结点结构如下所示。

其中:

    (1)ltag为0时指向该结点的左孩子,为1时指向该结点的前驱;

    (2)rtag为0时指向该结点的右孩子,为1时指向该结点的后继;

    (3)因此对于上图的二叉链表图可以修改为下图的养子。

由此二叉树的线索存储结构定义代码如下:

/* 二叉树的二叉线索存储结构定义 */
/* Link==0表示指向左右孩子指针 */
/* Thread==1表示指向前驱或后继的线索 */
typedef enum {Link, Thread} PointerTag;
/* 二叉线索存储结点结构 */
typedef struct BiThrNode
{
    TElemType data;    /* 结点数据 */
    struct BiThrNode *lchild, *rchild;    /* 左右孩子指针 */
    PointerTag LTag,PointerTag RTag;    /* 左右标志 */
} BiThrNode, *BiThrTree;

线索化的实质就是将二叉链表中的空指针改为指向前驱或后继的线索。由于前驱和后继的信息只有在遍历该二叉树时才能得到,所以线索化的过程就是在遍历的过程中修改空指针的过程。

中序遍历线索化的递归函数代码如下: 

BiThrTree pre == NULL;
/* 中序遍历进行中序线索化 */
/* 二叉树的二叉线索存储结构定义 */
/* Link==0表示指向左右孩子指针 */
/* Thread==1表示指向前驱或后继的线索 */
void InThreading(BiThrTree p)
{
    if (p)
    {
        InThreading(p->lchild);            /* 递归左子树线索化 */
        if (!p->lchild)    /* 没有左孩子 */
        {                    
            p->LTag = Thread;    /* 前驱线索 */
            p->lchild = pre;    /* 左孩子指针指向前驱 */
        }
        if (!pre->rchild)    /* 没有右孩子 */
        {
            pre->RTag = Thread;    /* 后继线索 */     
            pre->rchild = p;    /* 前驱右孩子指针指向后继(当前结点p) */
        }
        pre = p;    /* 保持pre指向p的前驱 */
        InThreading(p->rchild);    /* 递归右子树线索化 */
    }
}

 

遍历的代码如下:

/* T指向头结点,头结点左链lchild指向根结点,头结点右链rchild指向中序遍历的最后一个结点 */
Status InOrderTraverse_Thr(BiThrTree T)
{
    BiThrTree p;    /* p指向根结点 */
    p = T->lchild;    /* 空树或遍历结束时,p==T */
    while (p != T)
    {
        while (p->LTag == Link)    /* 当LTag==0时循环到中序序列第一个结点 */
            p = p->lchild;
        printf("%c", p->data);    /* 显示结点数据,可以更改为其他对结点操作 */
        while (p->RTag == Thread && p->rchild != T)
        {    
            p = p->rchild;
            printf("%c", p->data);
        }
        p = p->rchild;    /* p进至其右子树根 */
    }
    return OK;
}

 

二叉树与树的转化

树转换为二叉树

1.加线,在所有兄弟结点之间加一条连线。

2.去线,对树中每个结点,只保留它与第一个孩子结点的连线,删除它与其他孩子结点之间的连线

3.层次调整。以树的根结点为轴心,将整棵树顺时针旋转一定的角度,使之结构层次分明,注意第一个孩子是二叉树结点的左孩子,兄弟转换过来的孩子是结点的右孩子

 

二叉树转换为树

1.加线,若某结点的左孩子结点存在,则将这个左孩子的右孩子结点、右孩子的右孩子结点、右孩子的右孩子的右孩子结点......哈,反正就是左孩子的n个右孩子结点都作为此结点的孩子,将该结点与这些右孩子结点用线连接起来

2.去线,删除原二叉树中所有结点与其右孩子结点的连线

3.层次调整,使之结构层次分明。

 

森林转换为二叉树

1.把每个树转换为二叉树

2.第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子,用线连接起来。当所有的二叉树连接起来后就得到了由森林转换来的二叉树。

 

二叉树转换为树森林

1.从根结点开始,若右孩子存在,则把与右孩子结点的连线删除,再查看分离后的二叉树,若右孩子存在,则连线删除,直到所有右孩子连线都删除为止,得到分离的二叉树

2.再将每棵分离后的二叉树转换为树即可。

posted @ 2018-07-20 14:30  梦醒时夜续  阅读(216)  评论(0编辑  收藏  举报