二叉树的存储结构和基本运算

顺序存储结构

完全二叉树

完全二叉树的性质

一个编号为i的节点

  • 其双亲节点的编号为i/2
  • 左孩子编号为2i
  • 右孩子编号为2i+1
    在这里插入图片描述
    在这里插入图片描述
    不使用下标为0的位置

非完全二叉树

用空节点将非完全二叉树补为完全二叉树
在这里插入图片描述
使用特殊字符标记补齐的空节点

特点

  • 完全二叉树非常适合用顺序存储结构
  • 用顺序存储结构存储一般的二叉树会造成大量的空间浪费
  • 在顺序存储结构中,找到节点的双亲(i/2)和孩子(左孩子2i右孩子2i+1)比较容易

链式存储结构

借鉴孩子兄弟表示法的思想,在节点中设置左孩子指针和右孩子指针

typedef int ElemType;

typedef struct _btnode{
    ElemType data;
    struct _btnode *lchild, *rchild;
}btnode;

在这里插入图片描述

特点

  • 在存储非完全二叉树时比顺序结构节省空间
  • 找一个节点的孩子容易,找双亲不容易
  • 空指针个数 = 节点数 - 1

n个节点,对应2n个指针域,n-1个分支,所以非空指针域有n-1个,综上空指针域等于2n-(n-1)=n+1

遍历

用图形表示树形结构固然直观,但这样的示意图对计算机不太友好,计算机只会做循环和判断,也就是说它只会处理线性序列。从根节点出发,按照一定次序访问二叉树中的所有节点,使得每个节点只被访问一次,让二维的树形结构变成一个一维的线性序列。

先序遍历

若树不空

  1. 先访问根节点
  2. 遍历其左子树
  3. 遍历其右子树

先序遍历

遍历顺序是ABDEHCFIG

采用递归函数来实现这个算法

void
PreOrderTraversal( btnode *bt )
{
    if( bt == NULL ){
        return;
    }
    printf("%c", bt->data);
    PreOrderTraversal( bt->lchild );
    PreOrderTraversal( bt->rchild );
}

中序遍历

  • 先遍历其左子树
  • 访问根节点
  • 再遍历右子树

中序遍历

遍历顺序是DBHEAFICG

void
InOrderTraversal( btnode *bt )
{
    if( bt == NULL ){
        return;
    }
    InOrderTraversal( bt->lchild );
    printf("%c", bt->data);
    InOrderTraversal( bt->rchild );
}

后序遍历

  • 遍历左子树
  • 遍历右子树
  • 访问根节点

后序遍历
遍历顺序是DHEBIFGCA

void
PostOrderTraversal( btnode *bt )
{
    if( bt == NULL ){
        return;
    }
    PostOrderTraversal( bt->lchild );
    PostOrderTraversal( bt->rchild );
    printf("%c", bt->data);
}

层序遍历

层序遍历

前序、中序和后序遍历都是一棵子树一棵子树的遍历,双亲和孩子的信息都压入了栈中保存了起来,而层序遍历按照层而不是子树的顺序访问,如果访问完了左孩子访问右孩子的时候,没有记录左孩子的信息,那么左孩子下面的子树将无法访问。所以层序遍历需要单独设置队列来保存节点信息。

  • 从树的第一层,也就是根节点开始
  • 从上到下逐层遍历
  • 在同一层中,按从左到右的顺序逐个访问

算法实现思路

  1. 从队列中取出一个元素
  2. 访问该元素指向的节点
  3. 若该元素所指向的左、右孩子节点非空,则将其左、右孩子的指针按顺序入队
void
LevelOrderTraversal( btnode *bt )
{
    btnode *t;
    lQueue *q;

    do{
        //初始化一个链队
        q = ( lQueue* )malloc( sizeof( lQueue ) );
        q->front = q->rear = NULL;
    }while(0);

    if( bt == NULL ){	//若为空树直接返回
        return;
    }

    enQueue( q, bt );	//根节点进队

    while( q->rear != NULL ){
        btnode *t = q->front->address;	//队头的树节点
        if( t->lchild ){
            /* 有左孩子则进队*/
            enQueue( q, t->lchild );
        }
        if( t->rchild ){
            /* 有右孩子则进队 */
            enQueue( q, t->rchild );
        }
        deQueue( q );
    }
}

建立二叉树

把二维的树形结构转换为一维的线性序列之后,就可以把二叉树的节点信息扔给计算机处理了。

/* 需要输入一个前序遍历的二叉树序列 */
void
createTree( btnode **t )
{
    char ch;
    scanf("%c", &ch);
    if( ch == '#' ){
        *t = NULL;
    }else{
        *t = ( btnode* )malloc( sizeof( btnode ) );
        (*t)->data = ch;
        createTree( &( (*t)->lchild ) );
        createTree( &( (*t)->rchild ) );
    }
}

输入样例是一个补齐之后的二叉树先序遍历序列(用#表示空节点)
在这里插入图片描述

ABD##EH###CF#I##G##

销毁二叉树

递归调用函数,找到叶子节点之后返回,销毁左子树;再进入另一棵子树,找到叶子节点之后返回,销毁右子树。

对应的递归模型
当 t = N U L L , 不 做 任 何 事 情 其 他 情 况 , f ( t ) = f ( b → l c h i l d ) ; f ( b → r c h i l d ) ; f r e e ( t ) ; \begin{aligned} 当t&=NULL,不做任何事情\\ 其他情况,f(t)&=f(b→lchild);\\ &f(b→rchild);\\ &free(t); \end{aligned} t,f(t)=NULL,=f(blchild);f(brchild);free(t);

void
destroyTree( btnode *t )
{
    if( t == NULL ){
      return;
    }
    destroyTree( t->lchild );
    destroyTree( t->rchild );
    free( t );
}

输出所有的叶子节点

度为0的节点即为叶子节点,对应到代码上就是左孩子指针和右孩子指针都是NULL,在printf前面加上条件判断即可。

void
PreOrderPrintLeaves( btnode *bt )
{
    if( bt == NULL ){
        return;
    }
    if( !bt->lchild && !bt->rchild ){
        printf("%c", bt->data);
    }
    PreOrderPrintLeaves( bt->lchild );
    PreOrderPrintLeaves( bt->rchild );
}

中序后序层序都可以完成这个问题的求解。

求高度

整棵树的高度是一个大问题,左子树和右子树的高度是小问题。而整棵树的高度即为在左子树和右子树高度中取一个最大值。

对应的递归模型

当 t = N U L L , f ( t ) = 0 其 他 情 况 , f ( t ) = max ⁡ { f ( b → l c h i l d ) , f ( b → r c h i l d ) } + 1 \begin{aligned} 当t&=NULL,f(t)=0\\ 其他情况,f(t)&=\max\{f(b→lchild),f(b→rchild)\}+1 \end{aligned} t,f(t)=NULL,f(t)=0=max{f(blchild),f(brchild)}+1

int
PostOrderGetHeight( btnode *bt )
{
    int h_left, h_right;
    if( bt == NULL ){
        return 0;
    }
    h_left = PostOrderGetHeight( bt->lchild );
    h_right = PostOrderGetHeight( bt->rchild );

    return ( h_left > h_right ) ? ( h_left + 1 ) : ( h_right + 1 );
}
posted @ 2020-08-06 14:44  LanceHansen  阅读(177)  评论(0)    收藏  举报