数据结构:二叉树

来源:互联网 发布:网络教学管理平台 编辑:程序博客网 时间:2024/06/09 23:50

1. 树的定义与抽象数据类型

(1) 树的定义

  树(Tree)是n(n>=0)个结点的有限集。n=0时称为空树。在任意一棵非空树中:

  1. 有且仅有一个特定的称为根(root)的结点;
  2. 当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、……、Tm,其中每一个集合本身又是一棵树,并且称为根的子树(subTree)。

  需要注意的是:

  • n>0时根结点是唯一的,不可能存在多个根结点,别和现实中的大树混为一起,现实中的树有很多根须,那是真实的树,数据结构中的树是只能有一个根结点;
  • m>0时,子树的个数是没有限制,但它们一定不相交。

(2) 树的抽象数据类型

ADT 树(Tree)
Data
  树是由一个根结点和若干棵子树组成。树中结点具有相同数据类型及层次关系。
Operation
  initTree(&T):构造空树T。
  destoryTree(&T):销毁树T。
  createTree(&T, definition):按definition中给出树的定义来构造树。
  clearTree(&T):若树T存在,则将树T清为空树。
  treeEmpty(T):若T为空树,返回true,否则返回false。
  treeDeep(T):返回T的深度。
  root(T):返回T的根结点。
  value(T, cur_e):cur_e是树T中的一个结点,返回此结点的值。
  assign(T, cur_e, value):给树T的结点cur_e赋值为value。
  parent(T, cur_e):若cur_e是树T的非根结点,则返回它的双亲,否则返回空。
  leftChild(T, cur_e):若cur_e是树T的非叶子结点,则返回它的最左孩子,否则返回空。
  rightSibling(T, cur_e):若cur_e有右兄弟,则返回它的右兄弟,否则返回空。
  insertChild(&T, &p, i, c):其中p指向树T的某个结点,i为所指结点p的度加上1,非空树c与T不相交,操作结果为插入c为树T中p指结点的第i棵子树。
  deleteChild(&T, &p, i):其中p指向树T的某个结点,i为所指结点p的度,操作结果为删除T中p所指结点的第i棵子树。
endADT

(3) 树的存储结构

双亲表示法

struct  NodeType{
   ElemType  data ;    //该结点的数据
   int Parent;   //该结点的父亲的数组下标,对于根结点该域为0
 }Node;
 struct TreeType
   int r,n;      //根的位置 树的结点的总数目
   NodeType Node[MaxNodeCount];  //存储树的结点
 } 

双亲孩子表示法


去掉parent数据域便是孩子表示法

孩子兄弟表示法


2. 二叉树的定义及性质

(1) 二叉树的定义

  二叉树(Binary Tree)是n(n >= 0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。

  特点:

  1. 每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点。注意不是两棵子树,而是最多有。没有子树或者只有一棵子树都是可以有。
  2. 左子树和右子树是有顺序的,次序不能颠倒。
  3. 即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。

  二叉树的五种基本形态:

  1. 空二叉树;
  2. 只有一个根结点;
  3. 根结点只有左子树;
  4. 根结点只有右子树;
  5. 根结点既有左子树,又有右子树。

(2) 一些特殊的二叉树

  斜树:

  顾名思义,斜树一定要是斜的,但是往哪斜还是有讲究的。所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。

  满二叉树:

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

  单是每个结点都存在左右子树,不能算是满二叉树,还必须要所有的叶子都在同一层上,这就做到了整棵树的平衡。因此,满二叉树的特点有:

  1. 叶子只能出现在最下一层;
  2. 非叶子结点的度一定是2;
  3. 在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。

  完全二叉树:

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

  完全二叉树的特点:

  1. 叶子结点只能出现在最下两层;
  2. 最下层的叶子一定集中在左部连续位置;
  3. 倒数第二层,若有叶子结点,一定都在右部连续位置;
  4. 如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况;
  5. 同样结点数的二叉树,完全二叉树的深度最小。

(3) 二叉树的性质

  性质1:在二叉树的第i层至多有2i-1个结点(i >= 1)。

  满二叉树每一层结点数最多,可以用归纳法得到结论。

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

  由性质1可知:第1层最多1个结点,第2层最多2个结点,第3层最多3个结点,……,第k层至多2k-1个结点;

  则一共最多(1 + 2 + 4 + …… + 2k-1) = 2k-1 个结点。

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

  终端结点数其实就是叶子结点数,而一棵二叉树,除了叶子结点外,剩下的就是度为1或度为2的结点数了,我们设n1为度为1的结点数。则树T结点总数为n=n0+n1+n2

  则二叉树中总的入度数为n-1,因为除根结点外,每个结点入度均为1。

  二叉树中总的出度为n1+2n2,度为0的结点没有出度。

  显然二叉树中有 入度=出度,即n1+2n2=n-1=n0+n1+n2-1,则n0=n2+1。

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

  它的总结点数一定少于等于同样深度的满二叉树的结点数2k-1,但一定多于2k-1-1。

  即2k-1-1 < n <= 2k-1。

  也即2k-1 <= n < 2k

  两边取对数,可得到k-1 <= log2n < k

  而k作为深度数也是整数,因此,k=[log2n]+1。

  性质5:如果对一棵有n个结点的完全二叉树的结点按层序编号(从第1层到第[log2n]+1层,每层从左到右),对任一结点i(1<=i<=n)有:

  1. 如果i=1,则结点i是二叉树的根,无双新;如果i>1,则其双新结点为[i/2];

  2. 如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩子结点是2i;

  3. 如果2i+1>n,是结点i无右孩子;否则其右孩子为2i+1。

(4) 二叉树的存储结构

  二叉树的顺序存储结构

  二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置,也就是数组的下标要能体现结点之间的关系,比如双亲与孩子的关系,左右兄弟的关系等。对于完全二叉树而言,层序编号即可反应逻辑关系。

  对于一般的二叉树,尽管层序编号不能反映逻辑关系,但可以将其按完全二叉树编号,只不过,把不存在的结点设置为空而已。

  考虑一种极端的情况,一棵深度为k的右斜树,它只有k个结点,却需要分配2k-1个存储单元空间,这显然是对存储空间的浪费,所以,顺序存储结构一般只用于完全二叉树。

  二叉链表

  二叉树的每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域是比较自然的想法,我们称这样的链表叫做二叉链表。

  以下是我们的二叉链表的结点结构定义代码:

typedef struct BiTNode{    TElemType data;     struct BiTNode *lchild, *rchild; } BiTNode, *BiTree; 

  如果需要,还可以再增加一个指向其双亲的指针域,那样就称为三叉链表。

具体代码如下:

#include<stdio.h>#include<stdlib.h>#include<string.h>#define TRUE 1#define FALSE 0#define OK  1#define ERROR 0#define OVERFLOW -1#define INFEASIBLE -2#define Max_Tree_SIZE 20//最大结点数typedef int Status;typedef char TElemType;typedef struct BiTnode{    TElemType    data;    struct BiTnode *lchild,*rchild;}BiTNode,*BiTree;Status  InitBiTree(BiTree*);/*构造空树*/Status  CreateBiTree(BiTree*);/*先序递归建立二叉树*/Status  PreOrder(BiTree);/*先序递归遍历*/Status  InOrder(BiTree);/*中序递归遍历*/Status  PostOrder(BiTree);/*后序递归遍历*/Status  LevelOrder(BiTree);/*按层次非递归遍历*/Status  NRPreOrder(BiTree);/*先序非递归遍历二叉树*/int TreeDepth(BiTree);/*求高度*/int LeafNumber(BiTree);/*求叶结点书*/int main(){    BiTree BT;    TElemType ch;    int flag=1,select;    printf("To Creat binary tree,please input preorder with '#':");    InitBiTree(&BT);//初始化树    CreateBiTree(&BT);/*创建二叉树*/    while(flag==1)    {        printf("please select:\n");        printf("1. Preorder Traversal       \n");        printf("2. Inorder Traversal        \n");        printf("3. Postorder Traversal      \n");        printf("4. Levelorder Traversal     \n");        printf("5. NRPreorder Traversal     \n");        printf("6. TreeDepth                \n");        printf("7. LeafNumber               \n");        printf("8. Exit                     \n");        scanf("%d",&select);        switch(select){        case 1:printf("\nThe Preoder Traversal of binary Tree is:");                PreOrder(BT);                break;        case 2:printf("\nThe Inorder Traversal of binary Tree is:");                InOrder(BT);                break;        case 3:printf("\nThe Postorder Traversal of binary Tree is:");                PostOrder(BT);                break;       // case 4:printf("\nThe levelorder Traversal of binary Tree is:");                LevelOrder(BT);                break;       // case 5:printf("\nThe NRPreorder Traversal of binary Tree is:");                NRPreOrder(BT);                break;        case 6:printf("Binary tree's depth is %d",TreeDepth(BT));                break;        case 7:printf("\nThe number of leaf is:%d",LeafNumber(BT));                break;        default:flag=0;                printf("Press any key to exit");                getchar();        }//switch        printf("\n");    }//while    return 0;}//mainStatus InitBiTree(BiTree*  BT){    *BT=NULL;    return OK;}//构造空二叉树Status CreateBiTree(BiTree* BT){    TElemType ch;    scanf("%c",&ch);    if(ch=='#')        *BT=NULL;//注意是*BT    else{        *BT=(BiTree)malloc(sizeof(BiTNode));        (*BT)->data=ch;        CreateBiTree(&(*BT)->lchild);        CreateBiTree(&(*BT)->rchild);    }    return OK;}Status PreOrder(BiTree BT){    if(BT){        printf("%c",BT->data);        PreOrder(BT->lchild);        PreOrder(BT->rchild);    }    return OK;}Status InOrder(BiTree BT){    if(BT){        InOrder(BT->lchild);        printf("%c",BT->data);        InOrder(BT->rchild);    }    return OK;}Status PostOrder(BiTree BT){        if(BT){        PostOrder(BT->lchild);        PostOrder(BT->rchild);        printf("%c",BT->data);    }    return OK;}int TreeDepth(BiTree BT){     if(!BT)        return 0;    int d1,d2;    d1=TreeDepth(BT->lchild);    d2=TreeDepth(BT->rchild);    return (d1>d2?d1:d2)+1;}int LeafNumber(BiTree BT){    if(BT == NULL)        return 0;    return 1+LeafNumber(BT->lchild)+LeafNumber(BT->rchild);}



层次遍历如下:

void LevelOrderTraverse(BiTree T){    Queue BiNodeQueue;    BiTree p=T;    EnQueue(BiNodeQueue,p);    while(!BiNodeQueue.Empty())    {        DeQueue(BiNodeQueue,p);        if(p)        {            printf("%c",p->data);            EnQueue(BiNodeQueue,p->lchild);            EnQueue(BiNodeQueue,p->rchild);        }    }}
/*******************************************//*      递归法将二叉树的左右子树互换       *//*******************************************/void Exchange(BiTree T){    BiTree temp;    if(T)    {        Exchange1(T->lchild);        Exchange1(T->rchild);        temp=T->lchild;        T->lchild=T->rchild;        T->rchild=temp;    }}




原创粉丝点击