二叉树的存储结构


二叉树T:一个有穷的结点集合,这个集合可以为空。它是由根节点和称为其左子树TL和右子树TR的两个不相交的二叉树组成。二叉树的子树有左右顺序之分。

特殊二叉树:

斜二叉树

完美二叉树(满二叉树):都是两个度

完全二叉树不一定是满二叉树,编号一样就行,可以少

二叉树的几个重要性质

一个二叉树第i层的最大节点数为:2^(i-1),i>=1。

深度为k的二叉树有最大结点总数为:2^k -1,k>=1。

对任何非空二叉树T,若n0表示叶结点的个数,n2是度为2的非叶结点个数,那么两者满足关系n0=n2+1(叶子结点个数等于两个儿子的结点个数+1)

顺序存储结构

最适用:完全二叉树,按从上至下,从左到右顺序存储n个结点的完全二叉树的结点父子关系;

完全二叉树用数组实现的规律:

(1)非根节点的父结点的序号是[i/2];

(2)结点的左孩子结点序号是2i(若2i<=n,否则没有左孩子);

(3)结点的右孩子结点序号是2i+1(若2i+1<=n,否则没有右孩子);

一般二叉树也可以用数组,但会造成空间浪费

链表存储

typedef struct TreeNode *BinTree;

typedef BinTree Position;

struct TreeNode{

  ElementType Data;

  BinTree Left;

  BinTree Right;

}

二叉树的遍历

(1)先序遍历:访问根节点->先序遍历其左子树->先序遍历其右子树

void PreOrderTraversal( BinTree BT ){

  if ( BT ) {

    printf("%d", BT->Data);

    PreOrderTraversal ( BT->Left );

    PreOrderTraversal ( BT->Right );

  }

}

(2)中序遍历:中序遍历其左子树->访问根节点->中序遍历其右子树

void InOrderTraversal( BinTree BT ) {

  if ( BT ) {

    InOrderTraversal ( BT->Left );

    printf("%d", BT->Data);

    InOrderTraversal ( BT->Right );

  }

}

(3)后序遍历:后序遍历其左子树->后序遍历其右子树->访问根节点

void PostOrderTraversal( BinTree BT ) {

  if ( BT ) {

    PostOrderTraversal ( BT->Left );

    PostOrderTraversal ( BT->Right );

    printf("%d", BT->Data);

  }

}

确定一个二叉树至少需要两种遍历序列且一定要有中序遍历

二叉树的非递归遍历

(1)中序遍历非递归遍历算法:使用堆栈。遇到一个结点就压栈,并去遍历它的左子树,当左子树遍历结束后,从栈顶弹出这个结点并访问它,然后按其右指针再去中序遍历该结点的右子树

void InOrderTraversal( BinTree BT ) {

  BinTree T = BT;

  Stack S = CreatStack (MaxSize );  //创建并初始化堆栈S

  while ( T || !IsEmpty(S) ) {  //树不空或者堆栈不空

    while (T) {  //一直向左并将沿途结点压入堆栈

      Push(S, T);

      T = T->Left;

    }

    if ( !IsEmpty(S) ) {

      T = Pop(S);  //结点弹出堆栈

      printf("%5d", T->Data);  //(访问)打印结点

      T = T->Right;  //转向右子树

    }

  }

}

(2)先序遍历的非递归遍历算法:遇到一个结点,就把它压栈并访问它,当左子树遍历结束后,从栈顶弹出这个结点,然后按其右指针再去遍历该结点的右子树

void PreOrderTraversal( BinTree BT ) {

  BinTree T = BT;

  Stack S = CreatStack (MaxSize );  //创建并初始化堆栈S

  while ( T || !IsEmpty(S) ) {  //树不空或者堆栈不空

    while (T) {  //一直向左并将沿途结点压入堆栈

      printf("%5d", T->Data);  //(访问)打印结点

      Push(S, T);

      T = T->Left;

    }

    if ( !IsEmpty(S) ) {

      T = Pop(S);  //结点弹出堆栈

      T = T->Right;  //转向右子树

    }

  }

}

 层序遍历:从上至下,从左至右

二叉树遍历的核心问题:二维结构的线性化。

从结点访问其左、右儿子结点。同时需要一个堆栈或者队列保存暂时不访问的结点。

队列实现:遍历从根结点开始,首先将根结点入队,然后开始执行循环:结点出队、访问该结点、其左右儿子入队

层序基本过程:先根节点入队,然后:

(1)从队列中取出一个元素

(2)访问该元素所指结点

(3)若该元素所指结点的左、右孩子结点非空,则将其左、右孩子的指针顺序入队

void LevelOrderTraversal( BinTree BT ) {

  Queue Q;

  BinTree T;

  if ( !BT )  return;  //若是空树则直接返回

  Q = CreatQueue( MaxSize );  //创建并初始化队列Q

  AddQ ( Q, BT );  //入队

  while ( !IsEmptyQ( Q ) ) {

    T = DeleteQ ( Q );

    printf("%d\n", T->Data);  //访问取出队列结点

    if (T->Left)  AddQ( Q, T->Left );  //将其左右孩子入队

    if (T->Right)  AddQ( Q, T->Right );

  }

}