每日刷题191130 --基础知识篇 二叉搜索树

  休息了两天,状态恢复了一下,补充点基础知识。

二叉搜索树

  搜索树数据结构支持许多动态集合操作,包括Search,minimum,maximum,predecessor(前驱),successor(后继),INSERT和DELETE等。因此我们使用一颗搜索树既可以作为一个字典又可以作为一个优先队列。且二叉搜索树上的基本操作所花费的时间与这棵树的高度成正比。二叉搜索树有两个很重要的变体,红黑树与B树,这个我们之后有机会再补一篇文章。

  顾名思义,一棵二叉搜索树是以一棵二叉树来组织的。如图所示,这样的一棵树可以用一个链表的数据结构来表示,其中每个结点就是一个对象。除了结点的key之外,每个结点还包含属性left,right,p。分别用于指向这个结点的左孩子,右孩子与父节点。如果不存在,则对应的属性值为null。根结点是树种唯一父指针为null的结点。

  关于二叉搜索树有这么一些特点。对任何结点x,其左子树中的关键字最大值不超过x.key,其右子树中的关键字的最小值不小于x.key。不同的二叉搜索树可以代表同一组值的集合。如上图所示。

中序遍历

  二叉搜索树的这种性质允许我们通过一个简单的递归算法来按序(x.key的顺序)输出二叉搜索树的所有关键字。这种算法被称为中序遍历(inorder tree walk)。这样命名的原因是输出的子树的根的关键字位于根的左子树与右子树的关键字值之间。类似地,先序遍历,后序遍历输出的根的关键字在其左右子树的关键字值的前/后。一个中序遍历的伪算法如下:

INORDER-TREE-WALK(x)
{
    if (x != Null)
    {
          INORDER-TREE-WALK(x.left);
          print x;
          INORDER-TREE-WALK(x.right);
    }
}  

  对于之前图中的两棵二叉搜索树,他们中序遍历得到的结果是一样的,2,5,5,6,7,8.

查询二叉搜索树

  前面有提到,二叉搜索树支持许多集合操作。我们来看看一下利用二叉搜索树完成这些操作的思路。

  查找

    假如我们要寻找关键字值为k的结点,如果这个结点存在就返回,否则就返回null。可以利用下面的方法来进行查找。

Search(x,k) //x为要进行查找的树的根结点,k为要查找的关键字值
{
    if(x == Null || x.key ==k)
    {
        return x;
    }

    if(k < x.key)
    {
        return Search(x.left, k);
    }
    else
    {
        return Search(x.right, k);
    }
}

  思路很简单,我们判断当前这个结点是否是我们要寻找的结点,如果不是则决定使用左子树或右子树来继续查找。但上面的方式其实还是一种递归,我们可以用一种while循环的方式来改写它。对于计算机来说,这种方式效率应该会更高一些。

Search(x, k) //同样x为树的根结点,k为要查找的关键字的值
{
    while(x != Null && x.key != k)
    {
         if(k < x.key)
         {
             x = x.left;
         }
         else
        {
             x = x.right;
        }
    }

    return x;
}

  

  最大值与最小值

基于二叉搜索树的性质,对任何结点x,其左子树中的关键字最大值不超过x.key,其右子树中的关键字的最小值不小于x.key。这样我们去寻找一棵二叉搜索树种的最大值或最小值可以简化操作为不断的寻找右/左孩子,直到对应的右/左子孩子的值为null。以最大值举例,这样找到的结点值一定不小于它的父节点,而这个父节点也一定不小于它父节点,依次直到根结点。根结点又一定不小于左子树中的任意结点。

  后继与前驱

    我们先来看看后序与前驱的定义。给定一棵二叉搜索树种的一个结点,如果按照中序遍历的次序查找它的后继,如果所有的关键字互不相同,则一个结点x的后继是大于x的.Key的最小关键字的结点。同样,结点x的前驱是小于x.Key的最大关键字的结点。二叉搜索树的结构允许我们通过没有任何关键字的比较来确定一个结点的后继。如果后继存在,这样的过程将返回结点x的后继,否则就返回Null。

SUCCESSOR(x)
{
    if(x.right != null)
    {
        return TREE-MINIMUM(x.right); // 找到右子树中的最小值。
    }
    var y = x.p;

    while(y != null && x == y.right)
    {
        x = y;
        y = y.p;
    }

    return y;
}

  我们以上面寻找后继的伟算法为例,看看找后继结点的思路。前驱结点的寻找方式与之大同小异。根据后继的定义,它的值一定不小于x.key。因此,如果结点x的右子树存在,那么x.Right中的最小值就是x的后继。因为根据二叉搜索树的定义,这个最小值一定满足要求。如果x的右子树不存在,我们需要向x的祖先中寻找x的后继了。(x的左子树一定不满足要求,故舍去)。我们先找到x的父节点y,判读一下x是y的左孩子还是右孩子。如果x是左孩子,那x的父结点y就是x的后继。因为y.Key >= x.Key。如果x是y的右孩子,说明y的值仍小于x,此时我们应继续寻找。直到遇到根结点,或当期迭代的x是父节点的左孩子。

  另算法导论上有一个定理,即在一棵高度为h的二叉搜索树上,动态集合上的操作查找,最小值,最大值,寻找后继与前驱可以在O(h)的时间内完成。这里就不贴证明了。

原文地址:https://www.cnblogs.com/dogtwo0214/p/11963652.html

时间: 2024-10-11 19:35:08

每日刷题191130 --基础知识篇 二叉搜索树的相关文章

剑指Offer(Java版)第六十五题:给定一棵二叉搜索树,请找出其中的第k小的结点。 例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

/*给定一棵二叉搜索树,请找出其中的第k小的结点.例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4.*//*二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值*///思路:从最左边的叶子节点开始找起. import java.util.*; public clas

二叉搜索树以及对二叉搜索树平衡调整

代码的思想和图片参考:好大学慕课浙江大学陈越.何钦铭的<数据结构> 我们首先介绍一下什么是二叉搜索树和二叉平衡树: 二叉搜索树:一棵二叉树,可以为空:如果不为空,满足以下性质1. 非空左子树的所有键值小于其根结点的键值.2. 非空右子树的所有键值大于其根结点的键值.3. 左.右子树都是二叉搜索树. 二叉搜索树操作的特别函数:Position Find( ElementType X, BinTree BST ):从二叉搜索树BST中查找元素X,返回其所在结点的地址,查找的次数取决于树的高度  

【剑指Offer】二叉搜索树的后序遍历序列

问题描述: 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 背景知识: 二叉搜索树(Binary Search Tree),又叫二叉排序树:或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值: 它的左.右子树也分别为二叉排序树. 算法描述: 将数列分为三段,最后一段是最后一个数,也是树的

二叉搜索树 Rust实现

二叉搜索树 二叉搜索树是一颗二叉树 每个节点应该包含三个属性 left, right, p, 根节点p为NIL 设x是二叉搜索树的一个节点, y是x左子树的一个节点, 那么y.key <= x.key, 若y是x右子树的一个节点, 那么y.key >= x.key 遍历 遍历分前中后, 以根节点的遍历顺序而划分 之前写过一篇二叉搜索树的, 用C语言实现, 可以参考一下C语言实现 感受一下区别 以下是代码, 来源于RustPrimer type TreeNode<K, V> = Op

C#基础知识篇(二)-----------C#笔记

1.关系运算符(比较运算符) 1.关系运算符有哪些? >,< ==,!= >=,<= 2.关系运算符的作用? 用于比较两个事物之间的关系. 3.什么叫关系表达式? 由关系运算符连接起来的式子叫关系表达式. 注意:所有的关系表达式最终都能计算成一个bool类型的值. 2.逻辑运算符 1.逻辑表达式有哪些? 逻辑与:&& ,逻辑或:||  ,逻辑非:!(又叫取反) 2.逻辑运算 语法:表达式1 逻辑运算符  表达式2 逻辑运算符连接的两个表达式,要最终能求解成一个boo

C#基础知识篇(五)-----------C#笔记

一.值类型和引用类型 1>值类型和引用类型将我们学过的数据类型划分成了两部分. 划分的依据是不同类型的数据在内存中(堆栈)存储的结构不同. 2>值类型:所有的数值类型:long int short byte ulong uint ushort sbyte decimal duoble float char bool 枚举 结构 3>引用类型:string,arry(数组),类(class) 4>不管是值类型还是引用类型赋值都是将数据copy一份将副本赋给变量,不同的是值类型拷贝的是

C#基础知识篇(四)-----------C#笔记

一.类 1. 什么叫做类? 类是具有相同特征的一类事物统称.所以类是一种抽象,即不是一个实体(我们把类看做模板). 2. 什么叫做对象? 对象是根据类的模板创造出来的一个实体,它具有类里所有的特征,一个也多不得,一个也少不得.少了就不叫这个类的成员了,多了也不是!假如张三有变身这个功能,那么张三就不属于人. 记住对象是根据模板创建的,模板有什么它就有什么,不会多也不会少! 3. 什么叫做字段(或者是成员变量)? 我们把定义在方法的外面,类的里面(即:类中)的变量称之为字段或者说是成员变量. 4.

C#基础知识篇(三)-----------C#笔记

一.方法 1. 什么叫做方法? 方法就是对一段代码的重用的机制. 2. 方法的定义: [访问修饰符] [static] 返回值类型 方法名() { 方法体; } 注意:用[]修饰的都是可选的. 3. 需要注意的细节: 命名规则:方法名开头大写,参数名开头小写,参数名.变量名要有意义. 4. 方法的参数: 1>在方法名后面括号内定义变量就叫做定义这个方法的参数(形参). 2>在方法()中我们定义多个参数时,参数之间用逗号分隔,不管参数之间的类型是否相同,都不能像定义同类型的多个变量时:如:int

C#基础知识篇---------C#笔记

   一.变量         1.什么叫做变量?            我们把值可以改变的量叫做变量.          2.变量的声明:            语法:[访问修饰符] 数据类型 变量名; 如: int number=10://声明了一个整型的变量number.            注意:一次声明多个变量之间要用逗号分隔.                  如:int number1,number2,number3....;          3.变量的赋值: