二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法

这篇具有很好参考价值的文章主要介绍了二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

二叉排序树的定义

二叉排序树(Binary Sort Tree, BST),也称二叉查找树。
二叉排序树或者是一棵空树,或者是一棵具有下列特性的非空二叉树:
1) 若左子树非空,则左子树上所有结点关键字均小于根结点的关键字值;
2) 若右子树非空,则右子树上所有结点关键字均大于根结点的关键字值;
3) 左、右子树本身也分别是一棵二叉排序树。

由定义可知,二叉排序树是一个递归的数据结构,可以方便的使用递归算法对二叉排序树进行各种运算。
根据二叉树的定义,可得左子树结点值 < 根结点值 < 右子树结点值
所以,对二叉排序树进行中序遍历,可以得到一个递增的有序序列。

二叉排序树的结点定义:

typedef struct BSTNode{
    int data;
    struct BSTNode *left;
    struct BSTNode *right;
}BSTNode;

二叉树结点的创建:

//二叉树结点创建
BSTNode *CreateTreeNode(int x){
    BSTNode *p = (BSTNode *)malloc(sizeof(BSTNode));
    p->data = x;
    p->left = NULL;
    p->right = NULL;
    return p;
}

二叉排序树的查找

 二叉排序树的查找是从根结点开始的,沿某个分支逐层向下进行比较的过程。
 其查找过程描述如下:若二叉排序树非空,则将给定值与根结点的关键字比较,若相等,则查找成功;若不等,则当根结点的关键字值大于给定关键字值时,在根结点的左子树中查找;否则在根结点的右子树中查找。

实现代码:

//查找的递归算法
BSTNode *Search(BSTNode *root, int x){
    if(root->data == x){
        return root;
    }else if(x < root->data){
        return Search(root->left, x);
    }else{
        return Search(root->right, x);
    }
}
//查找的非递归算法
BSTNode *Search(BSTNode *root, int x){
    BSTNode *p = root;
    while(p!=NULL && p->data!=x){
        if(x < p->data)
            p = p->left;
        else
            p = p->right;
    }
    return p;
}

二叉排序树的插入

 二叉排序树作为一种动态集合,其特点是树的结构通常不是一次生成的,而是在查找过程中,当树中不存在关键字等于给定值的结点时再进行插入的。
 由于二叉排序树是递归定义的,因此插入结点的过程如下:若原二叉排序树为空,则直接插入结点;否则,若关键字<根结点关键字,则插入左子树,若关键字>根结点关键字,则插入右子树。

实现代码:

//插入的递归算法
BSTNode *Insert(BSTNode *root, int x){
    if(root == NULL){
        root = CreateTreeNode(x);
        return root;
    }
    if(x < root->data){
        root->left = Insert(root->left, x);
    }
    if(x > root->data){
        root->right = Insert(root->right, x);
    }
    return root;
}

二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法

//插入的非递归算法
BSTNode *Insert1(BSTNode *root, int x){
    BSTNode *parent = NULL;
    BSTNode *p = root;
    if(root == NULL){
        root = CreateTreeNode(x);
        return root;
    }
    while(p != NULL){
        parent = p;
        if(x < p->data){
            p = p->left;
        }else{
            p = p->right;
        }
    }
    if(parent->data >x){
        parent->left = CreateTreeNode(x);
    }else{
        parent->right = CreateTreeNode(x);
    }
    return root;
}

二叉排序树的构造

 构造一棵二叉排序树就是依次输入数据元素,并将它们插入二叉排序树中适当位置上的过程。
 构造二叉排序树的过程:每读入一个元素,就建立一个新结点,若二叉排序树为空,则将新结点作为二叉排序树的根结点;若二叉排序树非空,则将新结点的值与根结点的值比较,若小于根结点的值,则插入左子树,否则插入右子树。

实现代码:

void Create(BSTNode *&root, int str[], int n){
    root = NULL;
    for(int i=0; i<n; i++){
        Insert(root, str[i]);
    }
}

二叉排序树的删除

 在二叉排序树中删除一个结点时,不能把以该结点为根的子树上的结点都删除,必须先把被删除结点从存储二叉排序树的链表上摘下来,将因删除结点而断开的二叉链表重新链接起来,同时**确保二叉排序树的性质(左子树结点值<根结点值<右子树结点值)**不会丢失。

 删除操作一般会出现三种情况:
1) 若被删除结点z是叶结点,则直接删除,不会破坏二叉排序树的性质。
2) 若结点z只有一棵左子树或右子树,则让z的子树成为z父结点的子树,替代z的位置。
3) 若结点z有左、右两棵子树,则令z的直接后继(或直接前驱)替代z,然后从二叉排序树中删除这个直接后继(或直接前驱),这样就转换成了第一或第二种情况。

 注:已知二叉排序树经过中序遍历可以得到一个递增的有序序列,这里的直接后继(或直接前驱)应该是指被删除结点在这个中序遍历序列中的直接后继(或直接前驱)

 体现在二叉排序树的图中:
 某个结点的直接后继为以该结点为根的右子树中最左下位置的结点,即右子树的最小值;
 某个结点的直接前驱为以该结点为根的左子树中最右下位置的结点,即左子树的最大值。

下面依次给出以上三种情况的实例及删除操作:
<1> 被删除结点是叶结点,如删除关键字为23的叶子结点。
二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法

<2.1>被删除结点z只有一棵左子树,删除关键字为45的结点。
二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法

<2.2>被删除结点z只有一棵右子树,如删除关键字为78的结点。
二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法

<3>被删除结点z有左、右两棵子树,如删除结点78。
 再强调一下寻找替代被删除结点的结点的原则以供结合下图思考:

 已知二叉排序树经过中序遍历可以得到一个递增的有序序列,这里的直接后继(或直接前驱)应该是指被删除结点在这个中序遍历序列中的直接后继(或直接前驱)

 体现在二叉排序树的图中:
 某个结点的直接后继为以该结点为根的右子树中最左下位置的结点,即右子树的最小值;
 某个结点的直接前驱为以该结点为根的左子树中最右下位置的结点,即左子树的最大值。

二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法


实现代码:

//删除
bool Delete(BSTNode *p){
    //在二叉排序树中删除结点p, 并重新连接它的左右子树
    BSTNode *q, *s;
    //1.p为叶子结点
    if(p->left==NULL && p->right==NULL){
        p = NULL;
    }
    //2.1 p左子树为空, 重接右子树
    else if(p->left == NULL){
        q = p;
        p = p->right;
        free(q);
    }
    //2.2 p右子树为空, 重接左子树
    else if(p->right == NULL){
        q = p;
        p = p->left;
        free(q);
    }
    //3. p左右子树均不为空
    else{
        q = p;
        s = p->right; //找到p的右子树的最左端(中序直接后继)
        while(s->left != NULL){
            q = s;
            s = s->left;
        }
        p->data = s->data;
        
        if(q != p) //判断是否执行上述while循环
            q->left = s->right; //执行上述while循环,重接*q的左子树
        else
            q->right = s->right; //未执行上述while循环,重接*q的右子树,对于这个情况,可以参考代码后给出的示例图
        free(s);
    }
    return true;
}
bool DeleteBST(BSTNode *root, int x){
    if(root == NULL){
        return false;
    }else{
        if(x == root->data)
            return Delete(root); 
        else if(x < root->data)
            return DeleteBST(root->left, x);
        else
            return DeleteBST(root->right, x);
    }
}

对于未执行while循环的情况,如下图示例。
即未执行while循环的的情况为以被删除结点的右子树为根的树没有左子树只有右子树,也就是说,该右子树的最小值即为该根结点的值。
二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法

此外,在上面的代码中,在情况3中选择的是用被删除结点z的直接后继替代z,当然也可以用被删除结点z的直接前驱来替代,那么则需要修改为如下的代码即可。

//3. p左右子树均不为空
    else{
        q = p;
        s = p->left; //找到p的左子树的最右端(中序直接前驱)
        while(s->right != NULL){
            q = s;
            s = s->right;
        }
        p->data = s->data;
        
        if(q != p) //判断是否执行上述while循环
            q->right = s->left; //执行上述while循环,重接*q的右子树
        else
            q->left = s->left; //未执行上述while循环,重接*q的左子树
        free(s);
    }

完整代码及实例

#include<bits/stdc++.h>
using namespace std;

typedef struct BSTNode{
    int data;
    struct BSTNode *left;
    struct BSTNode *right;
}BSTNode;

#define N 100 

//查找的递归算法
BSTNode *Search(BSTNode *root, int x){
    if(root->data == x){
        return root;
    }else if(x < root->data){
        return Search(root->left, x);
    }else{
        return Search(root->right, x);
    }
}
//查找的非递归算法
BSTNode *Search1(BSTNode *root, int x){
    BSTNode *p = root;
    while(p!=NULL && p->data!=x){
        if(x < p->data)
            p = p->left;
        else
            p = p->right;
    }
    return p;
}

//二叉树结点创建
BSTNode *CreateTreeNode(int x){
    BSTNode *p = (BSTNode *)malloc(sizeof(BSTNode));
    p->data = x;
    p->left = NULL;
    p->right = NULL;
    return p;
}

//插入的递归算法
BSTNode *Insert(BSTNode *root, int x){
    if(root == NULL){
        root = CreateTreeNode(x);
        return root;
    }
    if(x < root->data){
        root->left = Insert(root->left, x);
    }
    if(x > root->data){
        root->right = Insert(root->right, x);
    }
    return root;
}
//插入的非递归算法
BSTNode *Insert1(BSTNode *root, int x){
    BSTNode *parent = NULL; //记录当前结点的父结点
    BSTNode *p = root;
    if(root == NULL){
        root = CreateTreeNode(x);
        return root;
    }
    while(p != NULL){
        parent = p;
        if(x < p->data){
            p = p->left;
        }else{
            p = p->right;
        }
    }
    if(parent->data >x){
        parent->left = CreateTreeNode(x);
    }else if(parent->data < x){
        parent->right = CreateTreeNode(x);
    }
    return root;
}


//构建
void Create(BSTNode *&root, int str[], int n){
    root = NULL;
    for(int i=0; i<n; i++){
        root = Insert(root, str[i]);
    }
}

//删除
bool Delete(BSTNode *p){
    //在二叉排序树中删除结点p, 并重新连接它的左右子树
    BSTNode *q, *s;
    //1.p为叶子结点
    if(p->left==NULL && p->right==NULL){
        p = NULL;
    }
    //2.1 p左子树为空, 重接右子树
    else if(p->left == NULL){
        q = p;
        p = p->right;
        free(q);
    }
    //2.2 p右子树为空, 重接左子树
    else if(p->right == NULL){
        q = p;
        p = p->left;
        free(q);
    }
    //3. p左右子树均不为空
    else{
        q = p;
        s = p->right; //找到p的右子树的最左端(中序直接后继)
        while(s->left != NULL){
            q = s;
            s = s->left;
        }
        p->data = s->data;
        
        if(q != p) //判断是否执行上述while循环
            q->left = s->right; //执行上述while循环,重接*q的左子树
        else
            q->right = s->right; //未执行上述while循环,重接*q的右子树
        free(s);
    }
    return true;
}
bool DeleteBST(BSTNode *root, int x){
    if(root == NULL){
        return false;
    }else{
        if(x == root->data)
            return Delete(root); 
        else if(x < root->data)
            return DeleteBST(root->left, x);
        else
            return DeleteBST(root->right, x);
    }
}


void LevelOrder(BSTNode *root){
    queue<BSTNode *> treenode; //队列存储结点
    if(root != NULL)
        treenode.push(root); //根结点入队
    while(!treenode.empty()){
        BSTNode *p = treenode.front(); 
        treenode.pop(); //根结点出队
        cout<<p->data<<" "; //输出队首元素,即当前访问的结点值
        if(p->left != NULL){
            treenode.push(p->left);//如果有左子树,则将左子树的根结点入队
        }
        if(p->right != NULL){
            treenode.push(p->right);//如果有右子树,则将右子树的根结点入队
        }
    }
}

int main(){
    BSTNode *root;
    int n;
    cin>>n;
    int str[n];
    for(int i=0; i<n; i++){
        cin>>str[i];
    }
    Create(root,str,n); 
    cout<<"当前二叉排序树的层序遍历序列为:";
    LevelOrder(root);
    cout<<endl;
    BSTNode *p = Search(root, 17);
    cout<<"结点17的右孩子结点值为:"<<p->right->data<<endl;
    DeleteBST(root, 78);
    cout<<"删除结点78后的二叉排序树的层序遍历序列为:";
    LevelOrder(root);
    return 0;
}

构建的二叉排序树如下图所示,删除时为删除结点78.
二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法

运行结果为:
二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法


二叉排序树的查找效率

二叉排序树查找算法的平均查找长度,主要取决于树的高度,即与二叉树的形态有关。


查找成功的平均查找长度为:  Σ(本层高度*本层结点个数) / 结点总数
查找不成功的平均查找长度为:   Σ(本层高度*本层补上的叶子结点个数) / 补上的叶子总数


如图所示的二叉排序树:
二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法

查找成功的平均查找长度为:(1*1 + 2*2 + 3*3 + 4*3)/ 9

二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法

查找不成功的平均查找长度:(2*1 + 3*3 + 4*6)/ 10文章来源地址https://www.toymoban.com/news/detail-478041.html

到了这里,关于二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 头歌 二叉树的二叉链表存储及基本操作

    第1关 先序遍历创建二叉链表存储的二叉树及遍历操作   第2关 计算二叉树的高度、总节点个数和叶子节点个数   第3关 层次遍历二叉树   第4关 递归实现二叉树左右子树交换   第5关 非递归实现二叉树左右子树交换   第6关 非递归实现二叉树的中序遍历

    2024年02月03日
    浏览(31)
  • 数据结构实验4:二叉树的基本操作

    一、问题描述 运用二叉链表实现二叉树的基本操作,包括:创建二叉树的存储结构、复制已有的二叉树、计算已有的二叉树的深度、先根序序列、中根序序列、后根序序列等。 输入格式:AB#C##D## 二、实验目的 掌握二叉链表及二叉树的基本操作。 三、实验内容及要求 1、构造

    2024年01月23日
    浏览(43)
  • 数据结构:二叉树的基本操作(用递归实现)

             本文将通过完成以下内容来展示二叉树的基本操作,代码解释标注全面而且清晰,代码书写也十分规范,适合初学者进行学习,本篇文章算是本人的一些学习记录分享,希望对有需要的小伙伴提供一些帮助~ 本文的内容为: 用递归的方法实现以下算法: 1.以二叉

    2024年02月06日
    浏览(50)
  • 【数据结构】二叉树的构建与基本操作实现

    👀 樊梓慕: 个人主页   🎥 个人专栏: 《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.前序建立二叉树 2.销毁二叉树 3.统计 4.查找值为x的节点 5.前中后序遍历 6.层序遍历 7.判断二叉树是否

    2024年02月07日
    浏览(45)
  • 【数据结构】二叉树的存储与基本操作的实现

    二叉树的存储结构分为: 顺序存储 和类似于 链表的链式存储 这里博主讲一下链式存储 二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有 二叉和三叉 表示方式 二叉表示: 三叉表示: 这里博主主要讲解一下孩子表示法 在学习二叉树的基本操作前,需

    2024年02月04日
    浏览(48)
  • 二叉树的基本操作-C语言实现-数据结构作业

    目录  (1)二叉树的创建; (2)二叉树的先序、中序和后序遍历输出; (3)输出二叉树的叶子节点和度为2的节点的数量; (4)输出二叉树的深度; (5)将二叉树所有节点的左右子树互换(左子树变右子树,右子树变左子树); (6)参考书上,二叉树按层次输出(一行输出一层); (7)删除二

    2024年02月04日
    浏览(48)
  • 数据结构实验报告,二叉树的基本操作(C语言)

    作者:命运之光 专栏:数据结构 实验六 二叉树的基本操作 实验环境:Visual C++或Dev C++ 实验目的: 1、掌握二叉树创建; 2、掌握二叉树的遍历及常用算法。 实验内容: 通过完全前序序列创建一棵二叉树,完成如下功能: 1)输出二叉树的前序遍历序列; 2)输出二叉树的中序遍

    2024年02月09日
    浏览(37)
  • 深入理解数据结构第三弹——二叉树(3)——二叉树的基本结构与操作

    二叉树(1): 深入理解数据结构第一弹——二叉树(1)——堆-CSDN博客 二叉树(2): 深入理解数据结构第二弹——二叉树(2)——堆排序及其时间复杂度-CSDN博客 前言: 在前面我们讲了堆及其应用,帮助我们初步了解了二叉树的一些原理,但那与真正的二叉树仍有不同,

    2024年04月09日
    浏览(49)
  • 数据结构-树的遍历和基本操作(Java实现)

    二叉树的遍历分为以下三种:  前序遍历: 访问顺序为  根节点----左子树----右子树 中序遍历: 访问顺序为  左子树----根节点----右子树 后序遍历: 访问顺序为  左子树----右子树----根节点 接下来针对这3种遍历方式进行详细介绍: 上图前序遍历顺序为 1 2 3 4 5 6 上图中序遍历顺序

    2024年03月25日
    浏览(44)
  • 【数据结构】【算法】二叉树、二叉排序树、树的相关操作

    树结构是以分支关系定义的一种层次结构,应用树结构组织起来的数据,逻辑上都具有明显的层次关系。 操作系统中的文件管理系统、网络系统中的域名管理、数据库系统中的索引管理等都使用了树结构来组织和管理数据。 树 Tree 是由n个节点组成的有限集合。在任意一颗非

    2024年02月04日
    浏览(54)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包