数据结构(树与二叉树)

做~自己de王妃 提交于 2019-11-26 12:29:34

一、定义

树是n(n≥0)个结点的有限集合,n=0时,称为空树。(它是一种一对多的逻辑结构)

而对任意非空树应该满足:

1)有且仅有一个特定的称为根的结点。

2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集合,其中每一个集合本身又是一棵树,称为根结点的子树。

(其中A为根节点。BEF为根节点A的其中一个子树。)                                                 观察可知树有以下两个特点:                                                                                        1。树的根节点没有前驱结点,除根节点外的所有结点有且只有一个前驱结点。                2。树中所有结点可以有零个或多个后继结点                                                                     (重要结论:n个结点的树中只有n-1条边。)         

二、基本术语

1)祖先结点和子孙结点 、双亲结点和孩子结点、兄弟结点

由根节点出发为了找到K,所经过的路径为:ABEK因此E、B、A均为K的祖先结点,而K为A、B、E的子孙结点。看这样一个子树EKL,在这个子树中K、L为E的孩子结点,而E为K、L的双亲结点,且K与L互为兄弟结点。

2)度

树中一个结点的子节点的个数称为该结点的度(如A、D的度为3,B、E的度为2,C的度为1,而F、G、H、I、J、K、L的度为0),而树中的最大度数称为树的度(容易看出该树的度为3)

3)分支结点与叶子结点

度大于0的结点称为分支结点,而度为0的结点称为叶子结点。(如A、B、C、D、E均为分支结点而F、G、H、I、J、K、L均为叶子结点。)

4)结点的层次、高度、深度

结点的层次:由树根开始定义,根节点为第1层(有的题目根节点为第0层,需要灵活变通),它的子节点为第2层,依次类推。

结点的高度:由叶子结点出发,自底向上累加(如B结点的高度:由叶子结点K出发途经KEB,因此结点的高度为3)

结点的深度:由根节点出发,自顶向下累加(如B结点的深度:由根节点出发途经AB,因此结点的深度为2) 

树的高度(深度)是树结点的最大层数:如:此树的高度(深度)为4。

 

5)有序树与无序树:树中的结点从左到右是有次序的,不能交换,这样的树为有序树,反之为无序树。

如图:交换B、D结点的位置,若其为有序树,那么二者代表的是不同的树;若为无序树,则二者代表的是完全相同的树。

6)路径与路径长度

路径:树中两个结点之间的路径是由这两个结点之间所经过的结点序列(与边无关)构成的。树中的分支是有向的,即从双亲结点指向孩子结点,所以路径一定是自上而下的。(如:由A到E的路径为ABE,特别注意由于路径是自上而下的因此E结点是到不了F结点的。)

路径长度:路径上所经历边的个数。(如:由A到E的路径长度为2)

7)森林

森林是M(m≥0)棵互不相交的树的集合。把树的根节点删去就成了森林。反之,给n棵独立的树加上一个结点,并把这n棵树作为该节点的子树,则森林变成了树。

三、树的性质

1)树中的结点数等于所有结点的度数加1。(计算时不考虑叶子结点,因其度为0.)

 

 

四、二叉树

 1.定义:二叉树是n(n≥0)个结点的有限集合。

1)n=0时,二叉树为空

2)n>0时,由根节点和两个互不相交的被称为根的左子树与右子树组成。左子树和右子树也分别是一个二叉树。

(二叉树中不存在度大于2的结点,且二叉树的子树有左右之分,其次序不能任意颠倒。)

2.五种基本形态。

例:三个结点的二叉树有多少种? 

 

二叉树与度为2的有序树有何区别?

 1)二叉树可以为空,而度为2的有序树至少有三个结点。

 2)二叉树的孩子结点是始终有左右之分,而度为2有序树的孩子结点次序是相对的(度为2的有序树的某结点若只有一个孩子结点是不区分左右的)。

 3.特殊的二叉树

1)满二叉树:一棵高度为h,且含有2^h -1个结点的二叉树为满二叉树。即树中的每层均含有最多的结点。满二叉树的叶子结点都集中在二叉树的最下一层,且除叶子节点之外的每个结点度数均为2。

且若对其进行编号,则对于编号为i的结点,若有左孩子,其左孩子的编号为2i,其右孩子的编号为2i+1;其双亲的编号为i/2取下界。(或者分类讨论:若为左孩子求其双亲直接对其序号除以2即可,若为右孩子则需要对其减1再除以2.)

 

2)完全二叉树:设一个高度为h、有n个结点的二叉树,当且仅当每个结点都与高度为h的满二叉树中编号1~n的结点一一对应时,称为完全二叉树。

 性质:

1)若i≤n/2取下界,则结点i为分支结点,否则为叶子结点。

2)叶子结点只可能在层次最大的两层出现。对于最大层次的叶子结点,都依次排在最左边的位置上。

3)度为1的结点若存在,则可能有一个,且是编号最大的分支结点,并且孩子结点一定是左结点。

4)若出现某结点(编号为i)为叶子结点或只有左孩子,则编号大于i的结点均为叶子结点。

5)若n为奇数,则每个分支结点都有左右孩子;若n为偶数,则编号最大的分支结点(编号为n/2)只有左孩子,而其余分支结点左右孩子均有。

 

3)二叉排序树:一棵二叉树,若树非空则具有如下性质:对任意结点若存在左子树或右子树,则其左子树上所有结点的关键字均小于该结点,右子树上所有结点的关键字均大于该结点。

 4)平衡二叉树:树上任意结点的左子树与右子树的深度之差不超过1。(强调为任意结点而不是根节点,如红图根节点的左子树的左右子树深度之差为2,因此不是一棵平衡二叉树。)

 

4.二叉树的性质

 

 

 5.二叉树的存储结构

用一组连续的存储单元依次自上而下、自左至右存储完全二叉树上的结点元素。

 但是如何表示它们之间的逻辑关系呢?在完全二叉树中依次编号,对于结点i:若存在左孩子,则编号为2i;若存在右孩子,则编号为2i+1.(这里数组是从1的位置开始存放的,从而空出了0位置的存储单元,这里是为了方便利用性质)

而在非完全二叉树中,按照之前的方法是无法表示逻辑关系的。

 对于此类的非完全二叉树我们应该对它进行补全,添加一个不存在的空结点,并在数组中用0表示。

缺点:顺序存储因为要对非完全二叉树进行补全因此在最坏情况下会非常浪费存储空间,比较适合完全二叉树 
 

6.二叉树的链式存储

用链表来存放一棵二叉树,二叉树中每个结点用链表的一个链结点来存储。

typedef struct BiTNode{
ElemType data;  //数据域
struct BiTNode *lchild,*rchild;  //左、右孩子指针
}BiTNode,*BiTree

 

7.二叉树的遍历:按某条搜索路径访问树中的每个结点,树的每个结点均被访问依次,而且只访问一次。 

 

1)先序遍历:若二叉树非空,先访问根节点,先序遍历左子树后先序遍历右子树。

void PreOrder(BiTree T){
    if(T!=NULL){
        Visit(T);        ///访问根节点
        PreOrder(T->lchild);        //递归遍历左子树
        PreOrder(T->rchild);        //递归遍历右子树
    }
}

 

 

2)中序遍历 :若二叉树非空,中序遍历左子树后访问根节点而后中序遍历右子树。

void InOrder(BiTree T){
    if(T!=NULL){
        InOrder(T->lchild);        //递归遍历左子树
        Visit(T);                  //访问根节点  
        InOrder(T->rchild);        //递归遍历右子树
    }
}

 

 

3)后序遍历:若二叉树非空则后序遍历左子树而后后续遍历右子树,最后访问根节点。 

void PostOrder(BiTree T){
    if(T!=NULL){
        PostOrder(T->lchild);        //递归遍历左子树
        PostOrder(T->rchild);        //递归遍历右子树
        Visit(T);                    //访问根节点
    }
}

8.递归算法与非递归算法的转换。

例:中序遍历非递归算法

 借助栈

1)初始时依次扫描根节点的所有左侧结点并将它们一一进栈;

2)出栈一个结点,访问它;

3)扫描该节点的右孩子结点并将其进栈;

4)依次扫描右孩子结点的所有左侧结点并一一进栈;

5)反复该过程直到栈空为止。

Void InOrder2(BiTree T){       //二叉树中序遍历的非递归算法,需要借助一个栈
InitStack(s);BiTree p=T;      //初始化栈;P是遍历指针
    While(p||!IsEmpty(s)){     //栈不空或p不空时循环 
        if(p){                 //根指针进栈,遍历左子树
            push(S,p);         //每遇非空二叉树先向左走   
            p=p->lchild;
        }
        else{                  //根指针退栈,访问根节点,遍历右子树
            pop(S,p);visit(p);//退栈,访问根节点 
            p=p->rchild;       //再向右子树走
        }
    }
}  

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!