二叉树的存储结构和基本运算
顺序存储结构
完全二叉树
完全二叉树的性质
一个编号为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
遍历
用图形表示树形结构固然直观,但这样的示意图对计算机不太友好,计算机只会做循环和判断,也就是说它只会处理线性序列。从根节点出发,按照一定次序访问二叉树中的所有节点,使得每个节点只被访问一次,让二维的树形结构变成一个一维的线性序列。
先序遍历
若树不空
- 先访问根节点
- 遍历其左子树
- 遍历其右子树

遍历顺序是A、B、D、E、H、C、F、I、G
采用递归函数来实现这个算法
void
PreOrderTraversal( btnode *bt )
{
    if( bt == NULL ){
        return;
    }
    printf("%c", bt->data);
    PreOrderTraversal( bt->lchild );
    PreOrderTraversal( bt->rchild );
}
中序遍历
- 先遍历其左子树
- 访问根节点
- 再遍历右子树

遍历顺序是D、B、H、E、A、F、I、C、G
void
InOrderTraversal( btnode *bt )
{
    if( bt == NULL ){
        return;
    }
    InOrderTraversal( bt->lchild );
    printf("%c", bt->data);
    InOrderTraversal( bt->rchild );
}
后序遍历
- 遍历左子树
- 遍历右子树
- 访问根节点

 遍历顺序是D、H、E、B、I、F、G、C、A
void
PostOrderTraversal( btnode *bt )
{
    if( bt == NULL ){
        return;
    }
    PostOrderTraversal( bt->lchild );
    PostOrderTraversal( bt->rchild );
    printf("%c", bt->data);
}
层序遍历

前序、中序和后序遍历都是一棵子树一棵子树的遍历,双亲和孩子的信息都压入了栈中保存了起来,而层序遍历按照层而不是子树的顺序访问,如果访问完了左孩子访问右孩子的时候,没有记录左孩子的信息,那么左孩子下面的子树将无法访问。所以层序遍历需要单独设置栈或队列来保存节点信息。
- 从树的第一层,也就是根节点开始
- 从上到下逐层遍历
- 在同一层中,按从左到右的顺序逐个访问
算法实现思路
- 从队列中取出一个元素
- 访问该元素指向的节点
- 若该元素所指向的左、右孩子节点非空,则将其左、右孩子的指针按顺序入队
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(b→lchild);f(b→rchild);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(b→lchild),f(b→rchild)}+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 );
}

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号