数据结构07:查找[C++][朴素二叉排序树BST]

这篇具有很好参考价值的文章主要介绍了数据结构07:查找[C++][朴素二叉排序树BST]。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

数据结构07:查找[C++][朴素二叉排序树BST]

图源:文心一言

考研笔记整理8k+字,小白友好、代码可跑,请小伙伴放心食用~~🥝🥝

第1版:查资料、写BUG、画导图、画配图~🧩🧩

参考用书:王道考研《2024年 数据结构考研复习指导》

参考用书配套视频:7.3_1 二叉排序树_哔哩哔哩_bilibili

特别感谢: Chat GPT老师、文心一言老师~


📇目录

📇目录

🦮思维导图 

🧵基本概念

⏲️定义

🌰推算举栗

⌨️代码实现

🧵分段代码

 🔯P0:调用库文件

 🔯P1:定义结点与指针

 🔯P2:封装创建结点

 🔯P3:插入结点

 🔯P4:构造二叉树

 🔯P5:树的遍历

 🔯P6:结点查询

 🔯P7:结点删除

 🔯P8:main函数

🧵完整代码

 🔯P0:完整代码

 🔯P1:执行结果

🔚结语


🦮思维导图 

数据结构07:查找[C++][朴素二叉排序树BST]

备注:

  • 思维导图为整理王道教材第7章 查找的所有内容;
  • 本篇仅涉及到朴素二叉排序树(BST)的代码;
  • 后期会在博文列表中整理平衡树、红黑树的相关内容[交可爱Ada小助手布置的红黑树作业],也可能会增加B树、线性查找的相关内容~    //博文写作时间很长,但是愿意点赞的小伙伴很少,因此还在犹豫中~😶‍🌫️😶‍🌫️

🧵基本概念

⏲️定义

  • 二叉排序树,若树非空,满足下列特性:
    • 若左子树非空:左子树上所有结点的值<根结点的值;
    • 若右子树非空:右子树上所有结点的值>根结点的值;
    • 左、右子树分别是一棵二叉排序树。
  • 简而言之,满足“ 左子树结点值 < 根结点值<右子树结点值 ” 的二叉树。

树的插入操作:与结点进行比较,小于则向左子树遍历,大于则向右子树遍历,直到遍历为空结点时,插入该结点。因此,插入的结点均为叶子结点。

注意:同一个序列不同的输入顺序,可能会得到不同的二叉树~

🌰推算举栗

  • 输入序列为递增/递减序列,或者从两端向中间逼近,例如:{3、7、8、9、10、15}、{15、3、7、10、9、8},这种就很容易形成度为1的长树~
  • 输入序列为从中间向两段逼近,例如:{8、7、10、3、9、15},这种就很容易形成比较理想的宽树形[度为2],甚至是平衡树~ //这棵树的创建过程博文后会详细说明~

平衡树的定义,可见:🌸数据结构05:树与二叉树[C++]

数据结构07:查找[C++][朴素二叉排序树BST]

  • ASL:平均查找长度,计算方式为 Σ(第 i 层结点数 x i 的层高)/ (结点个数),表示整棵树所有查找过程中,进行关键字比较次数的平均值~
  • 通过图中比较,可知:
    • 左侧树的查找:接近链表,与顺序查找相似,时间复杂度近似O(n);
    • 右侧树的查找:接近平衡树,与折半查找相似,时间复杂度近似O(log n);
  • 因此,朴素二叉排序树的查找的时间复杂度在O(n)~O(log n)波动,可以通过尽量调整树的高度以降低时间复杂度[🌸涉及到平衡二叉树,算法会在下一篇博文中提到]~

下面我们以图中右侧的小树为例,说明如何创建及遍历朴素二叉排序树~


数据结构07:查找[C++][朴素二叉排序树BST]

图源:文心一言

⌨️代码实现

🧵分段代码

 🔯P0:调用库文件

此次用到输入输出流文件iostream与创造动态数组的向量vector~

#include <iostream>
#include <vector>

 🔯P1:定义结点与指针

typedef struct BSTNode {
    int data;    //数据域
    struct BSTNode *lchild, *rchild;    //左孩子指针、右孩子指针
}BSTNode,*BiTree;    //结点BSTNode,指针BiTree

 🔯P2:封装创建结点

创建结点的步骤在创建树时重复出现,因此使用函数封装~

思路:(1)创建结点、(2)赋值、(3)指针置空,(4)返回结点~

//创建结点
BSTNode* CreateNode(int data) {
    BSTNode* newNode = new BSTNode();    //新建结点
    newNode->data = data;         //赋值数据域
    newNode->lchild = nullptr;    //指针置空
    newNode->rchild = nullptr;
    return newNode;               //返回结点
}

 🔯P3:插入结点

采用递归的方式创建树,朴素二叉树插入的结点通常为叶节点:

  • 若二叉排序树为空,则创建根结点;若结点为空,则插入结点。
  • 若二叉树不为空:
    • 关键字 = 根结点值,树中已有此元素;
    • 关键字<根结点值,继续遍历左子树;
    • 关键字>根结点值,继续遍历右子树。
int BST_Insert(BiTree &tree, int key) {
    if (tree == NULL) {            //若树为空,创建根结点;若遍历到结点为空,插入结点
        tree = CreateNode(key);
        return 1;
    }
    else if(key == tree->data)     //若结点已在树中存在,返回错误
        return 0;

    else if(key < tree->data)      //若待插入结点<关键字
        return BST_Insert(tree->lchild,key);    //向左子树查找

    else                           //若待插入结点>关键字
        return BST_Insert(tree->rchild,key);    //向右子树查找
}

 🔯P4:构造二叉树

通过main函数中声明的树地址BiTree &tree,以及传入的整数型动态数组std::vector<int> &vec,通过调用P3插入结点的函数完成树的创建~

void Creat_BST(BiTree &tree, std::vector<int> &vec){
    tree = nullptr;    //初始时树置空

    //将main中传入的数组,每个元素以插入的操作完成树的创建
    for(int i = 0; i < vec.size(); i++){    
        BST_Insert(tree, vec[i]);
    }
}

具体的创建过程可参考下图~

数据结构07:查找[C++][朴素二叉排序树BST]

 🔯P5:树的遍历

传入树的根结点内存地址,由于二叉树遵循:“左<根<右” 的原则,因此可以通过二叉树的中序遍历完成,此处采用递归方式完成~

void InOrderTraversal(BiTree tree) {
    if (tree == nullptr)    //如果树为空,返回
        return;

    InOrderTraversal(tree->lchild);    //遍历左子树

    std::cout << tree->data << " ";    //输出当前结点的值

    InOrderTraversal(tree->rchild);    //遍历右子树
}

 如果我没记错的话,运行起来应该是这样的~

  • 树指针的路径一路向左,结点8、结点7、结点3[以上3个结点入系统调用栈],结点3没有左孩子,打印结点3,结点3出栈;
  • 结点3没有右孩子结点,系统调用栈退1位,结点7出栈,打印结点7
  • 结点7没有右孩子结点,系统调用栈退1位,结点8出栈,打印结点8
  • 结点8没有右孩子结点,栈为空,向结点8的右子树结点10的左子树结点9移动[结点10、结点9入系统调用栈],结点9没有左孩子,打印结点9,出栈;
  • 结点9没有右孩子结点,系统调用栈退1位,结点10出栈,打印结点10
  • 结点10具有右孩子结点,打印结点10的右孩子结点15
  • 结点15没有孩子结点,且系统调用栈空,结束递归过程。

对于中序遍历原理感兴趣的小伙伴可以参考: 🌸数据结构05:树与二叉树[C++],该篇博文也含非递归方式先序、中序、后序遍历树的代码 [ 区别就是手动创建了一个调用栈 ]~

 🔯P6:结点查询

此处为了方便后序的删除操作,函数这边写成了结点的形式BSTNode*,需要传入两个值,树的地址BiTree tree和关键字int key~

  • 若树非空,且查询的值key与树中当前结点的值不同:
    • 查询的值key < 树中当前结点的值,向左子树走;
    • 查询的值key > 树中当前结点的值,向右子树走;
  • 若树为空或未找到结点,输出:未找到结点;
  • 除上述情况,输出:找到目标结点,与查询的值key 。
BSTNode* LocateElem(BiTree tree, int key) {
    while (tree != nullptr && key != tree->data) {    //树非空,且结点与关键字不等
        if (key < tree->data)    //关键字 < 结点,向左子树查询
            tree = tree->lchild;
        else                     //关键字 > 结点,向左子树查询
            tree = tree->rchild;
    }

    if (tree == nullptr) {    //数为空遍历结束,未找到结点,返回
        std::cout << "未找到目标节点\n";
        return nullptr;
    } else {                  //反馈信息:找到目标结点
        std::cout << "找到目标节点:" << tree->data << "\n";
        return tree;
    }
}

 🔯P7:结点删除

删除的情况略微复杂,传入树的地址BiTree tree和关键字int key~

删除可分为以下4种情况考虑——

  • 叶子结点:
    • 删除结点对于树的结构没有影响~
    • 当前孩子的父节点→NULL,并删除本结点~
  • 单左子树结点:
    • 删除结点时,其相邻的右孩子结点需要替代本结点的位置~
    • 当前孩子的父节点→当前孩子的子结点,并删除本结点~
  • 单右子树结点:
    • 删除结点时,其相邻的左孩子结点需要替代本结点的位置~
    • 当前孩子的父节点→当前孩子的子结点,并删除本结点~
  • 双子树结点:
    • ​​​​​​​删除结点时,其右子树最大的结点(即中序遍历后继结点)需要替代本结点的位置~
    • 当前结点的数值==中序遍历后继结点的数值~
    • 但中序遍历后继结点此时可能会有孩子结点,因此需要其当前孩子的父节点→当前孩子的子结点(同单子树结点)~

有点绕对不对,用图模拟一下这个过程,如果我没有理解错的话是这样的~

数据结构07:查找[C++][朴素二叉排序树BST]

void BST_Delete(BiTree& tree, int key) {
    if (tree == nullptr) {
        return;  // 树为空,直接返回
    }

    BiTree parent = nullptr;  // 记录要删除结点的父节点
    BiTree current = tree;    // 当前遍历到的结点

    // 查找要删除的结点以及其父节点
    while (current != nullptr && current->data != key) {
        parent = current;

        if (key < current->data) {
            current = current->lchild;  // 向左子树查找
        } else {
            current = current->rchild;  // 向右子树查找
        }
    }

    if (current == nullptr) {
        return;  // 未找到要删除的结点
    }

    // 根据不同情况进行删除
    if (current->lchild == nullptr && current->rchild == nullptr) {
        // 叶子结点,直接删除
        if (parent == nullptr) {
            tree = nullptr;  // 删除的是根结点
        } else if (parent->lchild == current) {
            parent->lchild = nullptr;  // 删除的是左子结点
        } else {
            parent->rchild = nullptr;  // 删除的是右子结点
        }

        delete current;  // 释放内存
    } else if (current->lchild == nullptr) {
        // 左子树为空,用右子树替代当前结点
        if (parent == nullptr) {
            tree = current->rchild;  // 删除的是根结点
        } else if (parent->lchild == current) {
            parent->lchild = current->rchild;  // 删除的是左子结点
        } else {
            parent->rchild = current->rchild;  // 删除的是右子结点
        }

        delete current;  // 释放内存
    } else if (current->rchild == nullptr) {
        // 右子树为空,用左子树替代当前结点
        if (parent == nullptr) {
            tree = current->lchild;  // 删除的是根结点
        } else if (parent->lchild == current) {
            parent->lchild = current->lchild;  // 删除的是左子结点
        } else {
            parent->rchild = current->lchild;  // 删除的是右子结点
        }

        delete current;  // 释放内存
    } else {
        // 左右子树都不为空,用右子树中最小的结点替代当前结点
        BiTree minNode = current->rchild;  // 最小结点
        BiTree minNodeParent = current;    // 最小结点的父节点

        while (minNode->lchild != nullptr) {
            minNodeParent = minNode;
            minNode = minNode->lchild;
        }

        current->data = minNode->data;  // 用最小结点的值替代当前结点的值

        if (minNodeParent == current) {
            minNodeParent->rchild = minNode->rchild;  // 最小结点是当前结点的右子结点
        } else {
            minNodeParent->lchild = minNode->rchild;  // 最小结点是当前结点右子树的最左子结点
        }

        delete minNode;  // 释放内存
    }
}

 🔯P8:main函数

main函数除了P0~P6的函数调用,就创建了1棵树,以及示意性地增加了与删除了结点~

int main() {
    BiTree tree = nullptr;
    //BiTree root = nullptr;

    // 增加结点:动态数组
    std::vector<int> vec = {8, 7, 10, 3, 9, 15};
    Creat_BST(tree, vec);
    //root = tree;  // 记录根节点的指针

    std::cout << "\n";

    // 输出结点:中序遍历
    std::cout << "遍历二叉树: ";
    InOrderTraversal(tree);
    std::cout << "\n";

    // 按值查找[本例为7]
    int target = 7;
    LocateElem(tree, target);

    //插入结点
    int node2 = 11;
    BST_Insert(tree, node2);
    std::cout << "插入节点:" << node2 << "\n";

    //删除结点
    int node3 = 8;
    BST_Delete(tree, node3);
    std::cout << "删除节点:" << node3 << "\n";

    // 输出结点:中序遍历
    std::cout << "遍历二叉树: ";
    InOrderTraversal(tree);

    std::cout << std::endl;

    return 0;
}

🧵完整代码

 🔯P0:完整代码

为了凑本文的字数,我这里贴一下整体的代码,删掉了细部注释~

Ps:改掉了以往博文二叉树需要手动输入结点搭建的缺点,扔到C++在线网站就可以测~🫥🫥

//头文件
#include <iostream>
#include <vector>

//结点结构
typedef struct BSTNode{
    int data;
    struct BSTNode *lchild, *rchild;
}BSTNode,*BiTree;

//创建结点
BSTNode* CreateNode(int data) {
    BSTNode* newNode = new BSTNode();
    newNode->data = data;
    newNode->lchild = nullptr;
    newNode->rchild = nullptr;
    return newNode;
}

//插入结点
int BST_Insert(BiTree &tree, int key) {
    if (tree == NULL) {
        tree = CreateNode(key);
        return 1;
    }
    else if(key == tree->data)
        return 0;

    else if(key < tree->data)
        return BST_Insert(tree->lchild,key);

    else
        return BST_Insert(tree->rchild,key);
}

//创建树
void Creat_BST(BiTree &tree, std::vector<int> &vec){
    tree = nullptr;
    for(int i = 0; i < vec.size(); i++){
        BST_Insert(tree, vec[i]);
    }
}

//中序遍历
void InOrderTraversal(BiTree tree) {
    if (tree == nullptr)
        return;

    InOrderTraversal(tree->lchild);
    std::cout << tree->data << " ";
    InOrderTraversal(tree->rchild);
}

//按值查找
BSTNode* LocateElem(BiTree tree, int key) {
    while (tree != nullptr && key != tree->data) {
        if (key < tree->data)
            tree = tree->lchild;
        else
            tree = tree->rchild;
    }

    if (tree == nullptr) {
        std::cout << "未找到目标节点\n";
        return nullptr;
    } else {
        std::cout << "找到目标节点:" << tree->data << "\n";
        return tree;
    }
}

//删除结点
void BST_Delete(BiTree& tree, int key) {
    if (tree == nullptr) {
        return;
    }

    BiTree parent = nullptr;
    BiTree current = tree;  

    // 查找要删除的结点以及其父节点
    while (current != nullptr && current->data != key) {
        parent = current;

        if (key < current->data) {
            current = current->lchild;  
        } else {
            current = current->rchild; 
        }
    }

    if (current == nullptr) {
        return; 
    }

    // 根据不同情况进行删除
    if (current->lchild == nullptr && current->rchild == nullptr) {
        // 叶子结点,直接删除
        if (parent == nullptr) {
            tree = nullptr; 
        } else if (parent->lchild == current) {
            parent->lchild = nullptr; 
        } else {
            parent->rchild = nullptr; 
        }

        delete current;
    } else if (current->lchild == nullptr) {
        // 左子树为空,用右子树替代当前结点
        if (parent == nullptr) {
            tree = current->rchild; 
        } else if (parent->lchild == current) {
            parent->lchild = current->rchild; 
        } else {
            parent->rchild = current->rchild; 
        }

        delete current;
    } else if (current->rchild == nullptr) {
        // 右子树为空,用左子树替代当前结点
        if (parent == nullptr) {
            tree = current->lchild; 
        } else if (parent->lchild == current) {
            parent->lchild = current->lchild; 
        } else {
            parent->rchild = current->lchild; 
        }

        delete current; 
    } else {
        // 左右子树都不为空,用右子树中最小的结点替代当前结点
        BiTree minNode = current->rchild; 
        BiTree minNodeParent = current;  

        while (minNode->lchild != nullptr) {
            minNodeParent = minNode;
            minNode = minNode->lchild;
        }

        current->data = minNode->data; 

        if (minNodeParent == current) {
            minNodeParent->rchild = minNode->rchild; 
        } else {
            minNodeParent->lchild = minNode->rchild; 
        }

        delete minNode; 
    }
}

int main() {
    BiTree tree = nullptr;

    std::vector<int> vec = {8, 7, 10, 3, 9, 15};
    Creat_BST(tree, vec);

    std::cout << "\n";

    std::cout << "遍历二叉树: ";
    InOrderTraversal(tree);
    std::cout << "\n";

    int target = 7;
    LocateElem(tree, target);

    int node2 = 11;
    BST_Insert(tree, node2);
    std::cout << "插入节点:" << node2 << "\n";

    int node3 = 8;
    BST_Delete(tree, node3);
    std::cout << "删除节点:" << node3 << "\n";

    std::cout << "遍历二叉树: ";
    InOrderTraversal(tree);

    std::cout << std::endl;

    return 0;
}

 🔯P1:执行结果

运行结果如下图所示~

数据结构07:查找[C++][朴素二叉排序树BST]


🔚结语

博文到此结束,写得模糊或者有误之处,欢迎小伙伴留言讨论与批评,督促博主优化内容,不限于以下内容~😶‍🌫️😶‍🌫️

  • 有错误:这段注释南辕北辙,理解错误,需要更改~
  • 难理解:这段代码雾里看花,需要更换排版、增加语法、逻辑注释或配图~
  • 不简洁:这段代码瘠义肥辞,好像一座尸米山,需要更改逻辑;如果是C++语言,调用某库某语法还可以简化~
  • 缺功能:这段代码败絮其中,能跑,然而不能用,想在实际运行或者通过考试需要增加功能~
  • 跑不动:这不可能——好吧,如果真不能跑,告诉我哪里不能跑我再回去试试...

博文若有帮助,欢迎小伙伴动动可爱的小手默默给个赞支持一下~🌟🌟文章来源地址https://www.toymoban.com/news/detail-498683.html

到了这里,关于数据结构07:查找[C++][朴素二叉排序树BST]的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 二叉排序树的创建、插入、查找和删除【数据结构】

    若它的左子树不空,则左子树上所有结点的值均小于它根结点的值。 若它的右子树不空,则右子树上所有结点的值均大于它根结点的值。 它的左、右树又分为⼆叉排序树 二叉排序树也叫二叉查找树、二叉搜索树 题目描述 给出一个数据序列,建立二叉排序树,并实现插入功

    2024年01月24日
    浏览(31)
  • 二叉排序树的插入删除和查找(数据结构实训)(难度系数100)

    二叉排序树的插入删除和查找 pre: 前序遍历 in: 中序遍历 post:后序遍历 insert: 插入,本题中不会出现相同的元素 delete: 删除,删除成功输出TRUE,没有该元素则输出FALSE,删除的方法是如果有左子树,以左子树中最大值作为新的树根,否则,以右子树最小值作为树根。 search:

    2024年01月25日
    浏览(34)
  • 合肥工业大学 宣城校区 数据结构与算法实验 队列、二叉树、查找和排序

    1.实验目标 熟练掌握队列的顺序存储结构和链式存储结构。 熟练掌握队列的有关算法设计,并在循环顺序队列和链队列上实现。 根据具体给定的需求,合理设计并实现相关结构和算法。 2.实验内容和要求 循环顺序队列的实验要求 循环顺序队列结构和运算定义,算法的实现以

    2024年02月11日
    浏览(35)
  • C语言数据结构二叉排序树的建立、插入、删除、查找操作(原理+完整代码)

    1、若左子树不为空,左子树上所有节点值小于 它根节点的值 2、若右子树不为空,右子树上所有节点值大于 它根节点的值 3、每个节点的左右子树也是二叉排序树 目的:提高查找、插入、删除的速度(不是为了排序) 时间复杂度:由于查找性能取决于树的形态,所以

    2024年02月09日
    浏览(37)
  • 【数据结构】二叉搜索树BST的实现(递归)

    目录 1.概念 2.图解: 3.元素插入操作 1.思路分析: 2.代码展示: 4.元素查找操作 1.前提根节点不为空 2.代码展示: 5.查找BST中的最大最小值 代码展示: 6.删除BST中的最大最小值 代码展示: 7.删除BST中的任意元素 代码展示:   二叉搜索树又称二叉排序树,它或者是一棵空树,

    2023年04月09日
    浏览(22)
  • 数据结构07:查找[C++][B树Btree]

     图源:文心一言 考研对于B树的要求重点在推理手算的部分,只参考王道论坛咸鱼老师的视频就可以了;若时间非常充裕的小伙伴,也可以往下滑了解一下代码 ~🥝🥝 备注: 这次的代码是从这里复制的: B-tree (programiz.com)。因为代码比较复杂,我与AI修改的代码没有通过删

    2024年02月16日
    浏览(19)
  • 数据结构与算法07:高效的排序算法

    目录 归并排序 快速排序 桶排序 计数排序 基数排序 对比各类排序算法 每日一练:排序链表 在上一篇文章中分析了简单的三种排序算法:冒泡排序、插入排序、选择排序,这三种排序算法的时间复杂度都是O(n^2),效率不是很高。如果要对大规模的数据排序,可以考虑使用归

    2024年02月08日
    浏览(26)
  • 【数据结构】18 二叉搜索树(查找,插入,删除)

    二叉搜索树也叫二叉排序树或者二叉查找树。它是一种对排序和查找都很有用的特殊二叉树。 一个二叉搜索树可以为空,如果它不为空,它将满足以下性质: 非空左子树的所有键值小于其根节点的键值 非空右子树的所有键值都大于其根结点的键值 左、右子树都是二叉树 在

    2024年02月22日
    浏览(34)
  • 【数据结构(C++)】树型查找——二叉搜索树

    目录 1. 二叉搜索树 1.1 二叉搜索树的概念 1.2 二叉搜索树类模板 1.3 二叉搜索树的操作 1.3.1 查找 1.3.2 插入 1.3.3 删除 1.4 二叉搜索树的性能分析 2. 平衡二叉树 2.1 平衡二叉树的概念 2.2 平衡二叉树类模板 2.3 二叉搜索树的插入 3. 红黑树 3.1 红黑树的概念 3.2 红黑树类模板 二叉搜索

    2024年02月10日
    浏览(30)
  • 【数据结构】二叉查找树和平衡二叉树,以及二者的区别

    目录 1、二叉查找树 1.1、定义  1.2、查找二叉树的优点  1.2、查找二叉树的弊端 2、平衡二叉树 2.1、定义 2.2、 实现树结构平衡的方法(旋转机制) 2.2.1、左旋 2.2.2、右旋 3、总结        二叉查找树又名二叉排序树,亦称二叉搜索树。是每个结点最多有两个子树的树结构

    2024年02月20日
    浏览(34)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包