《算法之美》---二叉搜索树

MediaScanner-MediaPrivider-插拔U盘多媒体扫描并优化 有些行业上,需要涉及到Android系统插拔U盘并扫描数据,如音乐、视频并播放。原生系统MediaScanner扫描,MediaStore通过ContentProvider读取,也是可行的。 阅读详情

二叉排序树又叫二叉搜索树、二元查找树,它或者是一棵空树;或者是具有下列性质的二叉树:

1)每个节点有一个关键值,并且没有任意两个元素有相同的关键值;因此,所有关键值都是唯一的;

2)若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;

3)若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;

4 )它的左右子树也分别是二叉排序树。

二叉排序树是一种动态树表,其特点是:树的结构通常不是一次生成的,而是在查找过程中,当树中不存在关键字等于给定值的节点时再进行插入。新插入的节点一定是一个新添加的叶子节点,并且是查找不成功时查找路径上访问的最后一个节点的左孩子或右孩子节点。

 

中序遍历二叉排序树可得到一个关键字的有序序列,即一个无序序列可以通过构造一棵二叉排序树而变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。而且每次插入的新节点都是二叉排序树上的新的叶子节点,则在进行插入操作时,不必移动其他节点,仅需改动某个节点的指针,由空变非空即可。这就相当于在一个有序序列上插入一个记录而不需要移动其他记录。这表明二叉排序树既拥有类似于折半查找的特性,又采用了链表做存储结构,因此是动态查找表的一种合适的表示。

 

这里重点说一下二叉排序树的删除操作,考虑包含要删除元素的节点p的三种情况:1)p是树叶;2)p只有一个非空子树;3)p有两个非空子树。

1)p是树叶:可以用丢弃树叶节点的方法来处理,即只有把其父节点指向它的指针域置空,然后删除该节点即可;

2)p只有一个非空子树:如果p没有父节点(即p是根节点),则将p丢弃,p的唯一子树的根节点作为新的搜索树的根节点;如果p有父节点pp,则修改pp的指针,使pp指向p的唯一孩子,然后删除节点p;

3)p有两个非空子树:只需将要删除的元素替换成它的左子树中的最大元素或右子树中的最小元素。

注意:必须保证右子树的最小元素和左子树的最大元素既不会在没有子树的节点中,也不会在只有一个子树的节点中。可以按下述方法来查找到左子树中的最大元素:首先移动到子树的根,然后沿着各节点的右孩子指针移动,直到右孩子指针为0为止。

 

二叉排序树的一种实现代码如下:

二叉排序树的定义和实现都放在头文件BSTree.h中,因为C++编译器不支持对模板的分离式编译:

========================BSTree.h==========================

template<typename T> class BSTree;

 

template<typename T> struct TNode

{

private:

    TNode<T> *left;  //左子树指针

    TNode<T> *right; //右子树指针

public:

    friend class BSTree<T>;

    T data;      //数据域

    //构造函数

    TNode():left(NULL),right(NULL){}

    TNode(T item, TNode<T> *left1, TNode<T> *right1)

            : data(item), left(left1), right(right1)

    {}           

};

 

template<typename T> class BSTree //二叉搜索树类类模板定义

{

private:

    int size;

public:

    //构造函数,把树根指针置空

    BSTree(TNode<T> *&root){root = NULL; size = 0;}

   

    //被ClearBST()所调用的函数

    void DeleteTree(TNode<T> *&root);

   

    //释放空间

    void FreeBST(TNode<T> *&root);

   

    //求二叉搜索树中所有节点数

    int BSTSize();

   

    //判断二叉搜索树是否为空

    int BSTEmpty(TNode<T> *&root);

   

    //取根指针

    TNode<T> *GetRoot(TNode<T> *&root);

   

    //从二叉搜索树中查找元素

    TNode<T> *BSTLocate(TNode<T> *&root, T item);

   

    //向二叉搜索树中插入元素

    void BSTInsert(TNode<T> *&root, T item);

   

    //从二叉搜索树中删除元素

    void BSTDelete(TNode<T> *&root, T item);

   

    //中序遍历输出二叉搜索树中的所有节点

    void InOrder(TNode<T> *&root);

   

    //求二叉搜索树的深度

    int BSTreeDepth(TNode<T> *&root);

   

    //求二叉搜索树中所有节点数

    int BSTreeCount(TNode<T> *&root);

   

    //求二叉搜索树中所有叶子节点数

    int BSTreeLeafCount(TNode<T> *&root);

   

    //清除二叉搜索树,使之变成一棵空树

    void ClearBSTree(TNode<T> *&root);

};

 

template<typename T>

void BSTree<T>::DeleteTree(TNode<T> *&root)

{

     if(root == NULL)

         return;

     if(root->left != NULL)

         DeleteTree(root->left);

     if(root->right != NULL)

         DeleteTree(root->right);

     free(root); //注意要释放root

}

 

template<typename T>

void BSTree<T>::FreeBST(TNode<T> *&root)

{

     DeleteTree(root);    

}

 

template<typename T>

int BSTree<T>::BSTSize()

{

    return size;   

}

 

template<typename T>

int BSTree<T>::BSTEmpty(TNode<T> *&root)

{

    if(root == NULL)

        return 1;

    else

        return 0;   

}

 

template<typename T>

TNode<T>* BSTree<T>::GetRoot(TNode<T> *&root)

{

    return root;         

}

 

template<typename T>

TNode<T>* BSTree<T>::BSTLocate(TNode<T> *&root, T item)

{

    TNode<T>* tmp;

    tmp = root;

    while(tmp != NULL)

    {

        if(item == tmp->data)

            break;

        else

        {

            if(item < tmp->data)

                tmp = tmp->left;

            else

                tmp = tmp->right;   

        }         

    }    

    return tmp;    

}

 

template<typename T>

void BSTree<T>::BSTInsert(TNode<T> *&root, T item)

{

    TNode<T> *tmp, *p, *newNode;

    newNode = new TNode<T>;

    newNode->data = item;

    if(root == NULL) //树是空的

    {

        root = newNode;

        size++;

        return;       

    }    

   

    tmp = root;

    p = NULL;

    while(tmp != NULL)

    {

        p = tmp;

        if(item < tmp->data)

            tmp = tmp->left;

        else

            tmp = tmp->right;    

    }

    if(item < p->data)

        p->left = newNode;

    else

        p->right = newNode;

    size++;

}

 

template<typename T>

void BSTree<T>::BSTDelete(TNode<T> *&root, T item)

{

    TNode<T> *tmp, *p, *r, *pr;

    tmp = root;

    p = NULL;

    while(tmp != NULL) //查找要删除元素所在节点

    {

        if(item == tmp->data)

            break;

        else

        {

            p = tmp;

            if(item < tmp->data)

                tmp = tmp->left;

            else

                tmp = tmp->right;   

        }

    } 

    if(tmp == NULL)

        return;

    if(tmp->left == NULL) //左子树为空,注意tmp是要删除的元素所在节点

    {

        if(p == NULL) //tmp的父节点p为空,即tmp是根节点

            root = tmp->right;

        else if(p->left == tmp) //tmp是父节点p的左子树

            p->left = tmp->right; //tmp的父节点p指向tmp的指针改为指向tmp唯一的右子树

        else //tmp是父节点的右子树

            p->right = tmp->right; //tmp父节点p指向tmp的指针改为指向tmp唯一的右子树

       

        free(tmp);   //删除tmp节点               

    }  

    else //tmp左子树不为空,这里使用左子树的最大元素来替换tmp

    {

        pr = tmp;

        r = tmp->left; //r是tmp的右子树的根

        while(r->right != NULL) //查找tmp左子树最大元素,放在r中,pr是r的父节点

        {

            pr = r;

            r = r->right;              

        }   

        tmp->data = r->data; //tmp中的值替换为右子树最大值

        if(pr == tmp)

            pr->left = r->left;

        else

            pr->right = r->left;

        free(r);

    }

    size--;

}

 

template<typename T>

void BSTree<T>::InOrder(TNode<T> *&root)

{

    if(root != NULL)

    {

        InOrder(root->left);

        std::cout<<root->data<<"  ";

        InOrder(root->right);       

    }    

}

 

template<typename T>

int BSTree<T>::BSTreeDepth(TNode<T> *&root)

{

    if(root == NULL)

        return 0;

    //计算左子树深度

    int dpt1 = BSTreeDepth(root->left);

    //计算右子树深度

    int dpt2 = BSTreeDepth(root->right);

    //返回树的深度

    if(dpt1 > dpt2)

        return dpt1+1;

    else

        return dpt2+1;

}

 

template<typename T>

int BSTree<T>::BSTreeCount(TNode<T> *&root)

{

    if(root == NULL)

        return 0;

    else

        return (BSTreeCount(root->left) + BSTreeCount(root->right) + 1);

}

 

template<typename T>

int BSTree<T>::BSTreeLeafCount(TNode<T> *&root)

{

    if(root == NULL)

        return 0;

    else

    {

        if(root->left == NULL && root->right == NULL)

            return 1;

        else

        {

            BSTreeLeafCount(root->left) + BSTreeLeafCount(root->right);   

        }   

    }   

}

 

template<typename T>

void BSTree<T>::ClearBSTree(TNode<T> *&root)

{

    DeleteTree(root);

    root = NULL;

    size = 0;    

}

 

=======================测试代码TestCase.cpp====================

#include <iostream>

#include <iomanip.h>

#include <stdlib.h>

#include <conio.h>

#include "BSTree.h"

 

int main()

{

    TNode<char> *q;

    std::cout<<"运行结果:"<<std::endl;

    char data[50] = "abxycdMNefgzkl";

    BSTree<char> tree(q);

    for(int i=0; data[i] != '/0'; i++)

    {

        tree.BSTInsert(q, data[i]);       

    }

    std::cout<<"中序遍历二叉搜索树:"<<std::endl;

    tree.InOrder(q);

   

    std::cout<<"二叉搜索树的节点数=="<<tree.BSTSize()<<std::endl;

    std::cout<<"二叉搜索树的节点数=="<<tree.BSTreeCount(q)<<std::endl;

    std::cout<<"二叉搜索树的深度=="<<tree.BSTreeDepth(q)<<std::endl;

    std::cout<<"二叉搜索树的叶子节点数=="<<tree.BSTreeLeafCount(q)<<std::endl;

   

    std::cout<<"Press any key and delete 'd'"<<std::endl;

    getch();

    tree.BSTDelete(q, 'd');

    std::cout<<"中序遍历二叉搜索树:"<<std::endl;

    tree.InOrder(q);

   

    std::cout<<"二叉搜索树的节点数=="<<tree.BSTSize()<<std::endl;

    std::cout<<"二叉搜索树的节点数=="<<tree.BSTreeCount(q)<<std::endl;

    std::cout<<"二叉搜索树的深度=="<<tree.BSTreeDepth(q)<<std::endl;

    std::cout<<"二叉搜索树的叶子节点数=="<<tree.BSTreeLeafCount(q)<<std::endl;

   

    getch();

    tree.FreeBST(q);

   

    system("pause");

    return 0;

}

数据结构与算法之二叉搜索树插入、查询与删除 1 二叉搜索树(BSTree)的概念   二叉搜索树又被称为二叉排序树,那么它本身也是一棵二叉树,那么满足以下性质的二叉树就是二叉搜索树,如图: 若左子树不为空,则左子树上所有节点的值都小于根节点的值; 若它的右子树不为空,则它的右子树上所有节点的值都大于根节点的值; 它的左右子树也要分别是二叉搜索树。 2 二叉搜索树的插入 2.1 搜索   插入之前我们先来说说它的搜索... 阅读详情

相关推荐

算法导论 — 15.5 最优二叉搜索树

笔记   二叉搜索树满足如下性质:假设xxx是二叉搜索树中的一个结点。如果lll是xxx的左子树的一个结点,那么l.key≤x.keyl.key≤x.keyl.key ≤ x.key。如果rrr是xxx的右子树的一个结点,那么r.key≥x.keyr.key≥x.keyr.key ≥ x.key。      也就是说,二叉搜索树中的任意一个结点,它的左子树中的所有结点都不大于它,它的右子树中...

yangtzhou的专栏 2万+

Android MediaProvider详解(基础篇)

我们都知道Android有四大组件,Activity、Service、BroadcastReceiver、ContentProvider。MediaProvider就是Android系统中的一个数据库,类似的还有TelephonyProvider、CalendarProvider、ContactsProvider,这些数据库的源码都在/packages/providers/目录下。其中MediaPr

布拉德利_蔡的专栏 1万+

算法:最优二叉搜索树

算法设计第五次作业part2 1.纸面题:对最优二叉树和矩阵连乘两种算法验证四边形法则,如果符合四边形法则则举几个正例,如果不符合则举几个反例 四边形法则 i<i‘   j<j‘w(i,j)+w(i‘,j‘)≤w(i‘,j)+w(i,j‘) i < i^`\ \ \ j < j^` \\ w(i,j) + w(i^`,j^`) \le w(i^`,j) + w(i,j^`) i<i‘   j<j‘w(i,j)+

一把健 1727

二叉查找树

二叉查找树的基本操作

w007d的专栏 319

二叉搜索树中第 K 小的元素算法讲解

通过利用二叉搜索树中序遍历得到升序序列的特性,结合栈来实现非递归的中序遍历,我们可以高效地找到二叉搜索树中第K小的元素。掌握这个算法不仅有助于理解二叉搜索树的特性,在实际的算法题和应用场景中也有很大的帮助。希望本文对你理解和实现该算法有所帮助。

ly1379223878的博客 1144

二叉搜索树

定义 二叉搜索树(Binary Search Tree)或称二叉查找树,也称二叉排序树(Binary Sort Tree)。它或者是一棵空树,或者是具有下列性质的二叉树: 若左子树不空,则左子树上所有节点的值均小于它的根节点的值; 若右子树不空,则右子树上所有节点的值均大于它的根节点的值; 左、右子树也分别为二叉搜索树; 性质 二叉搜索树与普通二叉树相比,有一些优秀的特征或性质: 由于节点是有序排放的:左子树<根节点<右子树。故在查找一个节点的时候,只需先和根节点比较,再决定是进入左子树还是右子树查找。而

CCPP Blog 4363

【动态规划】最优二叉搜索树——算法设计与分析

介绍了最优二叉搜索树问题及其相关概念。并按照动态规划求解的基本步骤给出了算法策略,并进行了复杂度分析。

友人帐_的博客 8568

验证二叉搜索树算法讲解

节点的左子树只包含小于当前节点的数。节点的右子树只包含大于当前节点的数。所有左子树和右子树自身也必须是二叉搜索树。验证二叉搜索树是二叉树相关算法中的一个基础问题,通过递归法和中序遍历法可以有效地解决。递归法通过设置上下界来判断每个节点是否符合二叉搜索树的条件;中序遍历法则利用二叉搜索树中序遍历结果为升序序列这一特性来验证。在实际应用中,可以根据具体场景选择合适的方法。希望本文的讲解和代码示例能帮助大家更好地理解和掌握这一算法。

ly1379223878的博客 764

TypeScript算法题实战——二叉搜索树篇

二叉搜索树,也叫二叉查找树、二叉排序树,是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值。

白洞,白色的明天在等着我们 1771

二叉搜索树 算法题合集

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。 这个题目注意的点: 1 先找到根节点,左子树在前面,右子树在其后,然后递归判断。 2 如果是空数组,返回的应该是true,我先以为空数组是false,后来回想起二叉搜索树的定义:可以是一棵空树。

天天开心 599

自动驾驶中的多传感器融合实战:用卡尔曼滤波搞定毫米波雷达与摄像头航迹对齐

本文深入探讨了自动驾驶中多传感器融合的实战技术,重点介绍了如何使用卡尔曼滤波实现毫米波雷达与摄像头的航迹对齐。通过分析传感器特性差异、时空同步挑战及工程化实现步骤,提供了包括状态向量定义、观测矩阵设计和故障检测机制在内的完整解决方案,最终实现0.15米以内的高精度航迹对齐。

weixin_28728495的博客 410

动态规划算法求解最优二叉搜索树

二叉搜索树T是一棵所有结点的元素值互异的二元树,本文将通过最优二叉搜索树问题的求解,带大家进一步熟悉动态规划算法,同时给出C++代码实现。

fufufunny的博客 2441

Buck-Boost电路实战:如何用LM2596模块快速搭建升降压电源(附元件选型指南)

本文详细介绍了如何使用LM2596模块快速搭建Buck-Boost升降压电路,包括硬件选型、电路搭建、示波器实测和进阶技巧。通过实测数据和避坑指南,帮助电子工程师解决电压不稳定、效率低下等常见问题,提升DC-DC变换器的设计水平。

weixin_30485379的博客 110

golang算法二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。有效 二叉搜索树定义如下:节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。示例 1:输入:root = [2,1,3]输出:true示例 2:输入:root = [5,1,4,null,null,3,6]输出:false解释:根节点的值是 5 ,但是右子节点的值是 4。提示:树中节点数目范围在[1, 104] 内。

weixin_44575660的博客 962

告别GUI点点点:Formality形式化验证的TCL脚本自动化实战(附完整脚本)

本文详细介绍了如何通过TCL脚本实现Formality形式化验证的全流程自动化,显著提升验证效率并降低错误率。内容涵盖脚本框架解析、参数化设计、验证报告生成及团队协作策略,特别适合芯片设计工程师摆脱传统GUI操作,实现300%以上的效率提升。

weixin_33737134的博客 282

SAP CAP 编程模型简介

官网 CAP 是用于构建企业级服务和应用程序的语言、库和工具框架。它引导开发人员沿着经过验证的最佳实践和大量现成的解决方案来应对重复性任务的“黄金之路”。 基于 CAP 的项目受益于对领域的主要关注。我们没有深入研究过于技术性的学科,而是专注于在快速变化的云技术世界中加速开发和保护投资。 CAP 框架融合了经过验证和广泛采用的开源和 SAP 技术,如下图所示。 在开源技术之上,CAP主要增加了: 核心数据服务 (CDS) 作为我们用于域模型和服务定义的通用建模语言。 用于 Node.js 和 Ja

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1545

数据结构与算法——二叉搜索树

一、二叉搜索树 二叉搜索树(BST)满足的性质: 每个节点中的值必须大于(或等于)存储在其左侧子树中的任何值。 每个节点中的值必须小于(或等于)存储在其右子树中的任何值。 一个二叉搜索树的例子如下所示: 图1:二叉搜索树举例 因为二叉树的中序遍历会得到一个递增的序列,因此在二叉搜索树里中序遍历比较常用。 给定一个二叉树,我们要判断它是否是二叉搜索树。二叉搜索树的特征比较明显...

贾维斯的小屋 5340

YOLOv8-Pose 姿态识别 RK3588 实战:从模型训练到 RKNN 部署,精度与推理速度双提升

本教程从YOLOv8-pose模型的姿态识别训练,到RK3588的边缘量化部署,为你打造了一套“高精度+高实时性+低成本”的人体姿态分析解决方案。无论是健身动作纠正、智能安防行为检测,还是工业人员姿态监控,这套技术都能直接复用——你只需替换数据集与业务逻辑,即可快速落地专属的边缘AI姿态识别系统。如果你在实践中遇到任何卡点,欢迎随时交流。记住,人体姿态识别的价值在于“关键点准、推理快、部署易”,而这三者的平衡,正是你通过本教程能掌握的核心能力。

FJN110的博客 363

​​​【收录 Hello 算法】7.4 二叉搜索树

与插入节点类似,我们需要保证在删除操作完成后,二叉搜索树的“左子树 < 根节点 < 右子树”的性质仍然满足。然而,如果我们在二叉搜索树中不断地插入和删除节点,可能导致二叉树退化为图 7-23 所示的链表,这时各种操作的时间复杂度也会退化为 𝑂(𝑛)。如图 7-22 所示,二叉树的中序遍历遵循“左 → 根 → 右”的遍历顺序,而二叉搜索树满足“左子节点 < 根节点 < 右子节点”的大小关系。如图 7-19 所示,当待删除节点的度为 0 时,表示该节点是叶节点,可以直接删除。在代码实现中,需要注意以下两点。

让学习成为一种习惯 1894

YOLOv12改进策略【卷积层】| CVPR-2020 利用DynamicConv 动态卷积 结合A2C2f进行二次创新,提高精度

在大规模视觉预训练中,通常模型的性能受到数据、参数和FLOP三个关键因素的影响。一般来说,模型的参数数量越多,FLOP也越高,但在移动设备等对计算资源有限制的场景下,需要低FLOP的模型同时又希望模型能从大规模预训练中受益。传统的方法很难在增加参数的同时保持低FLOP,因此需要一种新的设计来解决这个问题,模块应运而生。

Limiiiing的博客 827

【问题解决】解决 Python `PermissionError` 权限不足:文件/进程/端口全场景修复

本文总结了Python程序中常见的权限问题及解决方案,包括文件、进程和端口权限错误。文件权限问题通常由文件占用、权限不足或路径错误引起;进程问题多因权限不足或系统限制;端口问题则与端口占用、防火墙或特权端口相关。解决方案提供了详细的代码实现:文件权限管理器类(FilePermissionManager)包含检查权限(check_permission)、修复权限(fix_permission)、安全写入(safe_write)和安全删除(safe_delete)等方法,通过异常处理和临时文件等技术确保操作安全

博客 357

二叉搜索树的算法实现

文章目录1 二叉搜索树简介2 二叉搜索树的算法实现2.1 节点结构体的定义2.2 二叉搜索树插入节点 1 二叉搜索树简介 当我们要在一组数中要找到 26?你该怎么找? 答案: 从左至右 或 从右至左遍历一次,找到这个数字。 当我们把数据进行排序(按照从小到大的顺序排列)后,再查找相应的这条记录?还是用上面的方法吗? 答案:最快的方式,是采用折半法(俗称二分查找)。 **思考:**当我们有新的数...

木子皿--啥都不会的菜鸟 855

VMware ESXi 直通GPU

虚拟机需要独享GPU资源,那么就需要用到DirectPath I/O (直通),ESXi hypervisor 把 GPU 设备直接映射成虚机中的 GPU 设备,供应用使用。注意,内存注意一定要勾选预留,否则开机后报错如下:无法打开虚拟机 win10 的电源。1.ESXi主机,管理,硬件,PCI设备,勾选GPU卡,切换直通,会提示要求重启主机,重启主机之后,该GPU设备直通一栏显示为“活动”。先确认虚拟机是否EFI引导:编辑虚拟机 - 虚拟机选项 - 引导选项 - 固件 - EFI。

liwension的博客 6293
上一篇: 《ASCE1885的网络编程》---地址解析协议
下一篇: 《算法之美》---二叉堆及其实现
ACE1985
博客等级 码龄17年 1307粉丝 308原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值