《数据结构与算法》之二叉树(补充树)

这篇具有很好参考价值的文章主要介绍了《数据结构与算法》之二叉树(补充树)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.树结构之二叉树操作

二叉树的查找

二叉搜索树,也称二叉排序树或二叉查找树

二叉搜索树:一棵二叉树,可以为空,如果不为空,应该满足以下性质:

  1. 非空左子树的所有结点小于其根结点的键值
  2. 非空右子树的所有结点大于其根结点的键值
  3. 左右子树都是二叉搜索树

对于二叉树的查找,其实沿用的是分治法的思想,所以我们的树一定是要排序好的,这样才能使用每次检索都少一半的数据量

《数据结构与算法》之二叉树(补充树)

 我们可以看到上面的二叉树,它满足根结点的左边都比根结点小,而右边又都比根结点大,所以它就是可以使用二叉树查找的

当我们要查找6时

首先,我们对根节点进行比较,发现6是大于5的所以,我们需要去根结点的右边也就是结点8

然后,结点8进行对比,发现它比结点8小,所以去结点的额左边也就是结点6

最后,结点6和查找键值6是一样的,所以找到了

以下是代码实现:

Position Find(ElementType x, BinTree BST) {
    if (!BST) {
        return NULL;
    }
    if (x > BST->Data) {
        return Find(x, BST->Right);
    }
    else if (x<BST->Data){
        return Find(x, BST->Left);
    }
    else{
        return BST;
    }
}

我们可以发现,使用递归实现的代码查找,其实是尾递归,对于尾递归,我们可以直接使用循环来做

 利用循环的代码实现:

//将尾递归转换为循环,效率高
Position IterFind(ElementType x, BinTree BST) {
    while (BST) {
        if (x > BST->Data) {
            BST = BST->Right;
        }
        else if(x<BST->Data){
            BST = BST->Left;
        }
        else
            return BST;
    }
    return NULL;
}

 二叉树的插入

 插入结点的关键在于要找到结点的位置,我们可以使用查找函数的思维,找到它的位置,然后再对应插入

《数据结构与算法》之二叉树(补充树)

 上面的图片,我们要插入元素7到树中去,

首先,元素7是大于根节点5的,所以元素7应该处于根结点的右边位置,就和结点8进行比较

然后,发现结点8是大于元素7的,所以我们元素7因该在结点8的左边,也就是到了结点6的位置,

最后,结点6左右无元素,且元素7大于结点6,所以元素7位于结点6的右子结点

然后是代码实现:

BinTree Insert(ElementType x, BinTree BST) {
    if (!BST) {
        BST = malloc(sizeof(struct TreeNode));
        BST->Data = x;
        BST->Left = BST->Right = NULL;
    }
    else {
        if (x < BST->Data) {
            BST = Insert(x, BST->Left);
        }
        else if (x>BST->Data) {
            BST = Insert(x, BST->Right);
        }
        return BST;
    }
}

 文章来源地址https://www.toymoban.com/news/detail-481363.html

 二叉树的删除

 删除的情况要复杂一些,因为我们的树分有子结点和无子结点,当然无子结点的肯定是很好删除的,主要是有子结点的怎么删除呢?

这就需要我们分情况讨论:

 无子结点时:

直接删除需要删除的结点,并修改其父结点的指针,置为NULL

《数据结构与算法》之二叉树(补充树)

 如上图,

当我们要删除结点4的时候,结点4自己是没有子结点的,

所以我们可以直接释放结点4,并且把结点3的右结点指针指向NULL

当有一个子结点的时候:

需要把要删除的结点的父结点指针,指向它的孩子结点,相当于中间少了一层

《数据结构与算法》之二叉树(补充树)

 如上图,

我们要删除结点3的时候,发现它是有一个结点4的,我们就不能直接把结点3删除了

而是要先把结点5的指向结点3的指针指向结点4

然后再释放结点3

当有两个子节点的时候:

这种情况使用的方法是,要么使用左子树的最大元素顶上去,要么使用右子树的最小元素顶上去

《数据结构与算法》之二叉树(补充树)

 这里需要删除的结点是8,

我们可以在左子树中去找到最大的元素,然后替换到结点8的值,然后删除结点7就可以了

还可以去右子树中找到最小的元素,然后替换掉结点8为最小结点的值,然后删除最小结点

注意:

删除结点时需要改变父结点指针指向为NULL

当删除的结点后面还有结点时,要一个一个的连接到前面来

然后就是代码实现:

// 查找最小结点
Position FindMin(BinTree BST) {
    if (!BST)
        return NULL;
    else if (!BST->Left)
        return BST;
    else
    {
        return FindMin(BST->Left);
    }
}


//删除结点
BinTree Delete(ElementType x, BinTree BST) {
    Position Temp;
    if (!BST)
        printf("要删除的元素未找到!");
    else if (x < BST->Data) {
        BST->Left = Delete(x, BST->Left);
        //在左子树查找要删除的元素
    }
    else if (x > BST->Data) {
        BST->Right = Delete(x, BST->Right);
        //在右子树查找要删除的元素
    }
    else
        //找到要删除的结点
    {
        if (BST->Left && BST->Right){
            //左右都有结点
            Temp = FindMin(BST->Right);
            //找右子树的最小元素
            BST->Data = Temp->Data;
            //把右子树最小元素覆盖到要删除的元素上
            BST->Right = Delete(BST->Data, BST->Right);
            //删除右子树的最小元素
        }
        else
        {// 只有一个结点的情况,或没有结点的情况
            Temp = BST;
            if (!BST->Left) {
                //右结点存在,或没有结点存在
                BST = BST->Right;
            }
            else if (!BST->Right) {
                //左结点存在,或没有结点存在
                BST = BST->Left;
            }
            free(Temp);
    }
    }
    
    return BST;
}

 

二.进阶之平衡二叉树(AVL树)

什么是平衡二叉树?

平衡二叉树(AVL树):空树,或者任一结点左,右子树的高度差绝对值不超过1,即|  BF(T)<=1  |

平衡因子:BF(T)  =  hL - hR    ,其中hL,hR是T的左子树高度和右子树高度

我们可以来看几个图分别一下是不是平衡二叉树:

《数据结构与算法》之二叉树(补充树)

 上图,对于结点5来说,左子树高度2,右子树高度3

 3 -  2  =  1 满足  |  BF(T)<= 1  | 

但是对于右子树的结点8来说,它的左子树高度是2,右子树高度为0

2-0 = 2就不满足  |  BF(T)<= 1  | 

所以这不是一棵平衡二叉树

《数据结构与算法》之二叉树(补充树)

 对于这颗树来说,

结点5 的左子树高度是2,右子树高度是 3,满足最小高度为小于等于 1

结点8 的左子树高度是2,右子树高度是1,满足最小高度为小于等于 1

 所以它是一棵平衡的二叉树

平衡二叉树的调整

平衡二叉树在建树的时候其实是平衡的,主要是在后期的插入和删除中,会破坏掉它的平衡,最常见的就是插入就破坏了平衡,即  |  BF(T)> 1  | ,它就不满足平衡二叉树了

所以我们在插入的时候,会有一个二叉树的平衡调整的过程

RR旋转(右单旋转)------

《数据结构与算法》之二叉树(补充树)

 RR旋转指的就是,破坏发生在右子树的右子树,

解决方式就是把被破环的结点,也就是结点7的右子树作为它们的父结点,相当于把结点7提起来做父结点

由于我们的平衡二叉树在调整的时候也需要注意满足查找二叉树的准则,所以对被提起来的结点,它左边的结点需要挂到它父结点的左边

也就是说,把结点8做父结点提起来的时候,结点8的左子树要挂在结点7的右边

LL旋转(左单旋转)------

《数据结构与算法》之二叉树(补充树)

 LL旋转指的是,破坏发生在左子树的左子树

解决方式就是把被破坏的结点的子结点,提起来当作父结点,而原来的父结点当作子结点

也就是结点8被当作父结点,而原来的结点7被落下去当子结点了

由于平衡二叉树在被调整以后依然要满足查找二叉树的准则,所以我们需要把被提起来的结点的右子树,放在落下去结点的左子树

也就是,原来结点8的右子树要放在现在结点9的左子树(存在的情况下)

LR旋转 ----------

《数据结构与算法》之二叉树(补充树)

 LR旋转指的是,破坏发生在左子树的右子树上

 解决方式就是发生把发生破坏的结点提起来左父结点,而原来的左子树结点继续做左子树结点,原来的父结点做右结点

注意这时的波坏结点可能是插入了结点的,如果它插入在左边,那么他就是原来左子树结点的右子树,如果它插在破坏结点的右边,那么它就应该在原父结点的左边

也就是,在结点8,结点5,结点7中,结点7要被提起来做父结点,结点5继续做它的左子树结点,而原来的父结点8做新父结点的右子树

对于新插入的元素6,由于它是插入在结点7的左边,所以它是结点5的右子树,当然他要是插在了结点7的右边,那么它就是结点8的左子树

解释如下:

《数据结构与算法》之二叉树(补充树)

 RL旋转  ---------

《数据结构与算法》之二叉树(补充树)

 RL旋转指的就是,破坏在右子树的左子树

解决方式就是把破坏结点提起来做父结点,原来的父结点做新父结点的左子树,而原来的右子树还是新父结点的右子树

由于我们平衡二叉树调整后还要满足查找二叉树,所以对于插入破坏结点左边的数要在新的左子树的右边,而插入到破坏结点右边的数要在新的右子树的左边

也就是,把结点6提起来做新的父结点,原父结点3做结点6的左子树,而右结点则还是结点7,

对于原来插入到结点6左边的数要在新的左子树3的右边,而插入在结点6的右边的,要在新的右子树的左边

解释如下:

《数据结构与算法》之二叉树(补充树)

三.树的应用

对于一棵二叉树来说,它可以有多个输入序列,但是,构成的可能是同一棵二叉树,那么我们怎么根据输入序列来判断是不是同一棵二叉树呢?

不建树的判别方法:

如 :

3,1,2,4

vs

3,4,1,2

由于第一个元素肯定是根结点,所以根结点就是3,并且比根结点小的在左边,比它大的在右边

所以被分成了

{1,2} ,3,{4}

{1,2} ,3,{4}

我们可以看到它们此时的序列是完全一样的,所以可以构成一棵二叉树

在如:

3,1,2,4

vs

3,2,4,1

一样的方法划分,3为根结点

{1,2},3,{4}

{2,1},3,{4}

对于左边来说,第一个元素是下一层的根结点,

所以一个是1为父结点,一个是2为父结点,显然不是同一颗二叉树

建一棵二叉树,其它序列来依次对比:

typedef struct TreeNo* Tree;
struct  TreeNo
{
    int v;
    Tree Left, Right;
    int flag;
};

 

这是我们需要使用到的结构

我们对于多个输入序列,只会构建一棵二叉树,然后就是让序列 对已经构建好的二叉树进行遍历,其中flag有重要的作用

《数据结构与算法》之二叉树(补充树)

 这种方法来判定二叉树是否同构的思想是,

我们已经构建好了一棵二叉树,然后只需要对输入序列依次访问,

如果被访问的结点flag为0,那么就给flag赋值为1,

如果不是被访问结点,但是访问过了也就是flag为1,那么可以向下寻找,

如果不是被访问的结点,但是flag也不为1,那么则表示不同构

原因是,它们两个结点的构建的位置不一样,可以看上面的图片,输入序列要先构建3为父结点,而已构建的二叉树则使用结点5作为父结点,说以两个二叉树不同构

 四.红黑树--拓展

 红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组

红黑树的特性:

  1. 根结点是黑色的
  2. 叶子结点(null值)黑结点
  3. 红结点的子结点必须是黑结点
  4. 新插入的结点是红色的结点
  5. 父结点到任一叶子结点黑结点数相同

红黑树也是一种自平衡的二叉树,但是它比二叉平衡树的条件更加宽泛,

二叉平衡树所要求的平衡是左子树和右子树的高度相差小于等于1

而红黑树的要求是父结点到叶子结点的黑节点数相同就可以了,这个条件其实是很宽泛的了,我们可以找出它的一种极端情况,那么就是左子树都是黑色,而右子树黑红交叉,

这种情况构建的红黑树,它右半边的结点数其实是左边的2n-1 

《数据结构与算法》之二叉树(补充树)

 假如有一颗这样的树,其实不存在,因为很多右子树,左子树都没数据,是构成不了红黑树的,

我们看到它左边的长度是3,而右边则是6,很显然比左边多出了一倍,那么还能叫自平衡树吗?

这就是红黑树的平衡,我们可以把红结点都去掉,发现黑节点其实是平衡的,更重要的是,就算加上红结点,其实上面的查找的时间复杂度也是O(2 * logn)

由于我们的常量是不用记录到时间复杂度的,所以依旧是 logn

所以红黑树给出的平衡指的是:左子树和右子树的结点相等或相差一倍,都是可以的

 为什么红黑树这样定义平衡呢?

我们可以直到AVL二叉树它的平衡过于严格,所以每次插入都可能有很大的变化,大多数插入的时间都花在了维持那严格的平衡上了

而宽泛的红黑树,则变动更少,它的平均性能又很高,它能保证很高的性能,而又插入不会频繁的发生变更,所以红黑树就使用自己定义的那套平衡机制

红黑树的构造过程:

《数据结构与算法》之二叉树(补充树)

 上面的图片就是红黑树的构造规则,我们可以简单的来实现一下:

《数据结构与算法》之二叉树(补充树)

--

--

--

 

到了这里,关于《数据结构与算法》之二叉树(补充树)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 数据结构之二叉树和平衡二叉树

    1、二叉树: 2、平衡二叉树:

    2024年04月17日
    浏览(17)
  • 11. 数据结构之二叉树

    11. 数据结构之二叉树

    上一节,简单概述了树这种数据结构,以及树结构向下,具有某些一些特征的树,比如二叉树,B树,B+树,堆等。其中,二叉树是一个很重要的模块。也是在一些技术面试中,可能会问到的问题。本节,我们就二叉树,做详细介绍。 二叉树是一个 逻辑结构 , 底层可以用数组

    2024年02月07日
    浏览(12)
  • 数据结构之二叉树(Java)

    数据结构之二叉树(Java)

    在这里先说明一下,结点和节点其实一样的,无须关注这个。 1. 概念:树是一种非线性的数据结构,它是由n个有限节点组成一个具有层次关系的集合。 如上图所示,把此种数据结构称作树是因为它看起来像一个倒挂的树。  2. 特点 有一个特殊的节点,称为根节点,它是唯一

    2024年02月07日
    浏览(9)
  • 数据结构之二叉树(详细版)

    数据结构之二叉树(详细版)

            二叉树作为数据结构的一种,尤为重要,下面是对二叉树的详细讲解。想要了解二叉树,首先要了解 二叉树的基本概念,以及创建二叉树的结构,再深层点,遍历二叉树的前序中序和后续,其次是层序,后面将会讲解如何计算二叉树的高和叶结点 等等。        

    2024年02月03日
    浏览(13)
  • 数据结构之二叉树简介

    二叉树是一种非线性数据结构,代表“祖先”与“后代”之间的派生关系,体现了“一分为二”的分治逻辑。与链表相似,二叉树的基本单元是节点,每个节点包含值,左子节点的索引,右子节点的索引 当给定一个二叉树的节点时,我们将该节点的左子节点及其以下节点形成

    2024年02月01日
    浏览(18)
  • 数据结构之二叉树的实现

    数据结构之二叉树的实现

    目录 前言 1. 二叉树的遍历 1.1二叉树的前、中、后序遍历 1.2 层序遍历 2.二叉树的实现 2.1 二叉树的结构 2.2构建二叉树  2.2 前序遍历的实现 2.3 中序遍历的实现 2.4 后序遍历的实现 2.5 计算树的节点个数 2.6 计算树的深度 2.7 计算叶子节点个数 2.8 计算树第k层的节点数 2.9 以内容

    2023年04月10日
    浏览(11)
  • 数据结构之二叉树的性质与存储结构

    数据结构之二叉树的性质与存储结构

      数据结构是程序设计的重要基础,它所讨论的内容和技术对从事软件项目的开发有重要作用。学习数据结构要达到的目标是学会从问题出发,分析和研究计算机加工的数据的特性,以便为应用所涉及的数据选择适当的逻辑结构、存储结构及其相应的操作方法,为提高利用

    2024年01月21日
    浏览(15)
  • 数据结构奇妙旅程之二叉树初阶

    数据结构奇妙旅程之二叉树初阶

    ꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶ 个人主页:xiaoxieʕ̯

    2024年01月19日
    浏览(15)
  • 数据结构之二叉树的数组表示

    若某节点的索引为 i ,则该节点的左子节点的索引为 2i+1 ,右子节点的索引为 2i+2 给定某节点,获取它的左右字节点,父节点 获取前序遍历,中序遍历,后序遍历,层序遍历

    2024年01月18日
    浏览(13)
  • 【数据结构之二叉树的构建和遍历】

    【数据结构之二叉树的构建和遍历】

    前言: 前篇学习了 数据结构之树和二叉树的基本概念知识,那么这篇继续完善二叉树的相关知识并完成实现二叉树的构建和遍历的内容。 / 知识点汇总 / 因为前篇已经非常详细的单独学习了数和二叉树的基本知识概念,那么这里就简单回顾一下即可。 概念 : 二叉树(Bina

    2024年02月21日
    浏览(12)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包