深入学习二叉树,树等知识

树是数据结构中非常重要的一个知识点。要熟悉掌握二叉树的知识。

深入学习二叉树(一) 二叉树基础 ---详细请点击前面链接

介绍一般二叉树、完全二叉树、满二叉树、线索二叉树霍夫曼树二叉排序树、平衡二叉树、红黑树、B树。

1.结点
树中的元素,包含一个数据及若干指向子树的分支。
2. 结点的度
结点的分支数目。
树中的结点数=树中所有结点的度之和+1。
3.叶子节点
度为0的结点,没有后继的结点。
4.根结点
树中唯一没有前驱的结点。
5.分支结点(非终端结点)
度不为0的结点。
6.孩子结点
一个结点的直接后继称为该结点的孩子结点。
7.双亲结点
一个结点的直接前驱称为该结点的双亲结点。
8.兄弟结点
同一双亲的孩子结点
9.祖先结点
从根结点到该结点的路径上的所有结点
10.子孙结点
一个结点的直接后继和间接后继。
11.根结点的层定义为1;根的孩子为第二层结点,以此类推
12.树的高度(深度)
树中最大的结点层次。
13.有序树和无序树
有序树:树中结点的子树从左到右是有次序的,不能交换,这样的树叫有序树。一般讨论有序树。
无序树:树中结点的子树没有顺序,可以任意交换,这样的树叫无序树。
14.森林
m棵树不相交的树的集合

树的定义:n(n>=0)个结点的有限集。

n=0时称为空树。在任意一颗非空树中:
1)有且仅有一个特定的称为根(Root)的结点;
2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、......、Tn,其中每一个集合本身又是一棵树,并且称为根的子树。

此外,树的定义还需要强调以下两点:
1)n>0时根结点是唯一的,不可能存在多个根结点,数据结构中的树只能有一个根结点。
2)m>0时,子树的个数没有限制,但它们一定是互不相交的。

什么是节点度

结点拥有的子树数目称为结点的

图2.2
图2.2

 

结点子树的根结点为该结点的孩子结点。相应该结点称为孩子结点的双亲结点
图2.2中,A为B的双亲结点,B为A的孩子结点。
同一个双亲结点的孩子结点之间互称兄弟结点
图2.2中,结点B与结点C互为兄弟结点。

结点层次

从根开始定义起,根为第一层,根的孩子为第二层,以此类推。
图2.3

图2.3 层示意图

 

 树的深度:树中结点的最大层次数称为树的深度或高度

二叉树

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

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

二叉树性质

1)在二叉树的第i层上最多有2^{i-1} 个节点 。(i>=1)
2)二叉树中如果深度为k,那么最多有2^{k}-1个节点。(k>=1)
3)n_{0}=n_{2}+1 n_{0}表示度数为0的节点数,n_{2}表示度数为2的节点数。
4)在完全二叉树中,具有n个节点的完全二叉树的深度为[\log_{2}n]+1,其中[\log_{2}n]是向下取整。
5)若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点有如下特性:

(1) 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
(2) 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
(3) 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。

斜树:所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。

 

满二叉树:在一棵二叉树中。如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
满二叉树的特点有:
1)叶子只能出现在最下一层。出现在其它层就不可能达成平衡。
2)非叶子结点的度一定是2。
3)在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。

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

若设二叉树的深度为k,除第 k 层外,其它各层 (1~k-1) 的结点数都达到最大个数,第k 层所有的结点都连续集中在最左边,这就是完全二叉树。

特点
1)叶子结点只能出现在最下层和次下层。
2)最下层的叶子结点集中在树的左部。
3)倒数第二层若存在叶子结点,一定在右部连续位置。
4)如果结点度为1,则该结点只有左孩子,即没有右子树。
5)同样结点数目的二叉树,完全二叉树深度最小。
:满二叉树一定是完全二叉树,但反过来不一定成立。

存储结构请见原文

二叉树遍历方法有

前序遍历、中序遍历、后序遍历、层序遍历

先序遍历 先根元素,左元素,最后右元素  ABDHIEJCFG

中序遍历 先左元素,根元素,最后右元素  HDIBJEAFCG

后序遍历 先左元素 ,右元素,最后根元素 HIDJEBFGCA

/*二叉树的前序遍历递归算法*/
void PreOrderTraverse(BiTree T)
{
    if(T==NULL)
    return;
    printf("%c", T->data);  /*显示结点数据,可以更改为其他对结点操作*/
    PreOrderTraverse(T->lchild);    /*再先序遍历左子树*/
    PreOrderTraverse(T->rchild);    /*最后先序遍历右子树*/
}


/*二叉树的中序遍历递归算法*/
void InOrderTraverse(BiTree T)
{
    if(T==NULL)
    return;
    InOrderTraverse(T->lchild); /*中序遍历左子树*/
    printf("%c", T->data);  /*显示结点数据,可以更改为其他对结点操作*/
    InOrderTraverse(T->rchild); /*最后中序遍历右子树*/
}


/*二叉树的后序遍历递归算法*/
void PostOrderTraverse(BiTree T)
{
    if(T==NULL)
    return;
    PostOrderTraverse(T->lchild);   /*先后序遍历左子树*/
    PostOrderTraverse(T->rchild);   /*再后续遍历右子树*/
    printf("%c", T->data);  /*显示结点数据,可以更改为其他对结点操作*/
}

层次遍历就是按照树的层次自上而下的遍历二叉树。针对图3.13所示二叉树的层次遍历结果为:ABCDEFGHIJ 

看完后多练习先序后序中序等遍历方法,已知先序和中序,画出二叉树结构等。

深入学习二叉树(二) 线索二叉树 ---详细请点击前面链接

先找到中序遍历的顺序,然后根据中序遍历方法来建立前驱结点和后继结点

二叉树的中序遍历结果为HDIBJEAFCG

图中黑色点画线为指向后继的线索,紫色虚线为指向前驱的线索。

我们如何区分一个结点的lchild指针是指向左孩子还是前驱结点呢?

为了解决这一问题,现需要添加标志位ltag,rtag。并定义规则如下:

ltag为0时,指向左孩子,为1时指向前驱
rtag为0时,指向右孩子,为1时指向后继

线索二叉树结点数据结构

//#define Link 0//指针标志  
//#define Thread 1//线索标志  
typedef char TElemType;   
//中序线索二叉树  
typedef enum PointerTag {Link, Thread};//结点的child域类型,link表示是指针,指向孩子结点,thread表示是线索,指示前驱或后继结点  
//定义结点数据结构
typedef struct ThrBiNode{  
    TElemType data;  
    ThrBiNode *lchild, *rchild;//左右孩子指针  
    PointerTag lTag, rTag;//左右标志  
}ThrBiNode, *ThrBiTree;  

中序遍历建立线索二叉树

//中序遍历进行中序线索化
void inThreading(ThrBiTree T, ThrBiTree &pre){  
    if(T){  
        inThreading(T->lchild, pre);//左子树线索化  
  
        if(!T->lchild){//当前结点的左孩子为空  
            T->lTag = Thread;  
            T->lchild = pre;  
        }else{  
            T->lTag = Link;  
        }  
  
        if(!pre->rchild){//前驱结点的右孩子为空  
            pre->rTag = Thread;  
            pre->rchild = T;  
        }else{  
            pre->rTag = Link;  
        }  
        pre = T;          
        inThreading(T->rchild, pre);//右子树线索化  
    }  
}  

 

//T指向头结点,头结点的lchild链域指针指向二叉树的根结点  
//中序遍历打印二叉线索树T(非递归算法)  
void inOrderTraversePrint(ThrBiTree T){  
    ThrBiNode *p = T->lchild;//p指向根结点  
      
    while(p != T){//空树或遍历结束时,p == T  
        while(p->lTag == Link){  
            p = p->lchild;  
        }  
        //此时p指向中序遍历序列的第一个结点(最左下的结点)  
  
        printf("%c ", p->data);//打印(访问)其左子树为空的结点  
  
        while(p->rTag == Thread && p->rchild != T){  
            p = p->rchild;  
            printf("%c ", p->data);//访问后继结点  
        }  
        //当p所指结点的rchild指向的是孩子结点而不是线索时,p的后继应该是其右子树的最左下的结点,即遍历其右子树时访问的第一个节点  
        p = p->rchild;  
    }  
    printf("\n");  
}  

深入学习二叉树(三) 霍夫曼树 ---详细请点击前面链接

霍夫曼树是二叉树的一种特殊形式,又称为最优二叉树,其主要作用在于数据压缩和编码长度的优化。

在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度

 结点的权及带权路径长度

若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。

树的带权路径长度

树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL ; WPL = 6 * 2 + 3 * 2 + 8 * 2 = 34;

给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为霍夫曼树(Huffman Tree)。

叶子结点为A、B、C、D,对应权值分别为7、5、2、4。
3.1.a树的WPL = 7 * 2 + 5 * 2 + 2 * 2 + 4 * 2 = 36
3.1.b树的WPL = 7 * 1 + 5 * 2 + 2 * 3 + 4 * 3 = 35

由ABCD构成叶子结点的二叉树形态有许多种,但是WPL最小的树只有3.1.b所示的形态。则3.1.b树为一棵霍夫曼树。

深入学习二叉树(四) 二叉排序树 ---详细请点击前面链接

二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),也称二叉搜索树。二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:

(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;

由二叉树的递归定义性质,二叉排序树的查找同样可以使用如下递归算法查找。

如果树是空的,则查找结束,无匹配。
如果被查找的值和根结点的值相等,查找成功。否则就在子树中继续查找。如果被查找的值小于根结点的值就选择左子树,大于根结点的值就选择右子树。

二叉树的删除可不再像二叉树的插入那么容易了,以为删除某个结点以后,会影响到树的其它部分的结构。
删除的时候需要考虑以下几种情况:

1)删除结点为叶子结点;
2)删除的结点只有左子树;
3)删除的结点只有右子树
4)删除的结点既有左子树又有右子树。

根据中序遍历来删除,然后前继后续

平衡二叉树详解  ---详细请点击前面链接

插入

因为新插入节点而导致平衡性被破坏的节点也叫麻烦节点,而被其破坏平衡的节点叫被破坏节点。 

一共把破坏平衡型的类型分为了四种,分别是LL型、RR型、LR型和RL型(L=left,R=right)。

LL型解决方案:以被破坏节点为基础进行右旋

LL型解决方案:以被破坏节点为基础进行左旋

LR型解决方案:以被破坏节点L(左)节点为基础先进行一次L(左)旋,再以被破坏节点为基础进行右旋。

RL型解决方案:以被破坏节点R(右)节点为基础先进行一次R(右)旋,再以被破坏节点为基础进行左旋。

处理方式分类两大类:

       1.RR型和LL型,以被破坏节点为基础进行其反向的旋转即可,即RR型进行左旋,LL型进行右旋。

       2.RL型和LR型,先以被破坏节点的LR或RL首字母的节点进行LR或RL首字母旋转,再以被破坏节点为基础进行LR或RL尾字母旋转,即RL型先以被破坏节点的R(右)节点为基础进行一次R(右)选,再以被破坏节点为基础进行一次L(左)旋;LR旋先以被破坏节点的L(左)节点为基础进行一次L(左)选,再以被破坏节点为基础进行一次R(右)旋。

删除

删除的节点在右子树:

第一种:删除后被破坏节点的左节点的左边高度大于右边高度。

第一种结论:当在右子树删除而导致树失衡时,判断被破坏节点的左节点的左边高度和右边高度,如果左边高度大于右边高度,则相当于在被破坏节点的左节点的左节点下插入一个新的节点而导致的不平衡,即LL型插入,此时进行右旋调整即可。

第二种:删除后被破坏节点的左节点的左边高度小于右边高度。

第二种结论:当在右子树删除而导致树失衡时,判断被破坏节点的左节点的左边高度和右边高度,如果左边高度小于右边高度,则相当于在被破坏节点的左节点的右节点下插入一个新的节点而导致的不平衡,即LR型插入,此时先对被破坏节点的L(左)节点进行L(左)旋,再对被破坏节点进行R(右)旋即可。

第三种:删除后被破坏节点的左节点的左边高度等于右边高度。

第三种结论:当在右子树删除而导致树失衡时,判断被破坏节点的左节点的左边高度和右边高度,如果左边高度等于右边高度,因为在右子树进行删除,此时对被破坏节点进行右旋即可。

删除的节点在左子树:

第一种:删除后被破坏节点的左节点的左边高度小于右边高度。

第一种结论:当在左子树删除而导致树失衡时,判断被破坏节点的右节点的左边高度和右边高度,如果左边高度小于右边高度,则相当于在被破坏节点的右节点的右节点下插入一个新的节点而导致的不平衡,即RR型插入,此时进行左旋调整即可。

第二种:删除后被破坏节点的左节点的左边高度大于右边高度。

第二种结论:当在左子树删除而导致树失衡时,判断被破坏节点的右节点的左边高度和右边高度,如果左边高度大于右边高度,则相当于在被破坏节点的右节点的左节点下插入一个新的节点而导致的不平衡,即RL型插入,此时先对被破坏节点的R(右)节点进行R(旋)旋,再对被破坏节点进行L(左)旋即可。

第三种:删除后被破坏节点的左节点的左边高度等于右边高度。

第三种结论:当在左子树删除而导致树失衡时,判断被破坏节点的左节点的左边高度和右边高度,如果左边高度等于右边高度,因为在左子树进行删除,此时对被破坏节点进行左旋即可。


链接地址 ---详细请点击前面链接

红黑树 ---详细请点击前面链接

红黑树就是一种平衡的二叉查找树,说他平衡的意思是他不会变成“瘸子”,左腿特别长或者右腿特别长。除了符合二叉查找树的特性之外,还具体下列的特性:

1. 节点是红色或者黑色

2. 根节点是黑色

3. 每个叶子的节点都是黑色的空节点(NULL)

4. 每个红色节点的两个子节点都是黑色的。

5. 从任意节点到其每个叶子的所有路径都包含相同的黑色节点。

红黑树能自平衡,它靠的是什么?三种操作:左旋、右旋和变色。

  • 左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。如图3。
  • 右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。如图4。
  • 变色:结点的颜色由红变黑或由黑变红。

红黑树查找

因为红黑树是一颗二叉平衡树,并且查找不会破坏树的平衡,所以查找跟二叉平衡树的查找无异:

  1. 从根结点开始查找,把根结点设置为当前结点;
  2. 若当前结点为空,返回null;
  3. 若当前结点不为空,用当前结点的key跟查找key作比较;
  4. 若当前结点key等于查找key,那么该key就是查找目标,返回当前结点;
  5. 若当前结点key大于查找key,把当前结点的左子结点设置为当前结点,重复步骤2;
  6. 若当前结点key小于查找key,把当前结点的右子结点设置为当前结点,重复步骤2;

 

B-树的详解

B+树的详细说明

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值