高级树结构之二叉查找树

news2024/11/25 10:34:48

文章目录

  • 一 二叉查找树简介
  • 二 创建和插入操作
  • 三 查找操作
    • 3.1 查找思路
    • 3.2 代码实现
  • 四 删除操作
    • 4.1 情况讨论
    • 4.2 代码实现
  • 五 完整代码
    • 5.1 二叉查找树的结构
    • 5.2 完整代码内容

一 二叉查找树简介

  • 二叉查找树【二叉搜索树或是二叉排序树】
    • 左子树中所有结点的值,均小于其根结点的值
    • 右子树中所有结点的值,均大于其根结点的值
    • 二叉搜索树的子树也是二叉搜索树
      在这里插入图片描述
  • 规则:二义查找树满足左边一定比当前结点小,右边一定比当前结点大
  • 比如需要在这颗树种查找值为15的结点
    1. 从根结点18开始,因为15小于18,所以从左边开始找
    2. 接着来到10,发现10比15小,所以继续往右边走
    3. 来到15,成功找到。

二 创建和插入操作

  • 结点的结构和二叉搜索树的创建
#include<iostream>
using namespace std;
typedef int E;
typedef struct TreeNode {
    E element;
    struct TreeNode *left;
    struct TreeNode *right;
} *Node;

Node createNode(E element) {
    Node node = (Node) malloc(sizeof(struct TreeNode));
    node->left = node->right = NULL;
    node->element = element;
    return node;
}
  • 插入
  • 二叉树不能插入重复元素,如果出现重复元素,则直接忽略
  • 关于代码实现部分,如果对递归不太熟悉的朋友可以运行完整代码进行调试,逐步进行有利于理解程序的运行!
Node insert(Node root,E element) {
    if(root) {
        if(element< root->element)
            root->left= insert(root->left,element);
        else if(element>root->element)
            root->right= insert(root->right,element);
    }else {//当节点为空时,说明已经找到插入的位置,创建对应的结点
        root= createNode(element);
    }
    return root;//返回当前结点
}

三 查找操作

3.1 查找思路

  • 查找的思路就是不断比较值的大小,直到找到数值
  • 如果要查找最大值,就不断往右边遍历;如果要查找最小值,就不断往左边遍历。

3.2 代码实现

Node find(Node root,E target) {
    while(root) {
        //如果要找的值比当前值小,就往左边查找
        //如果要找的值比当前值大,就往右边查找
        //否则,就是找到了
        if(target<root->element) {
            root=root->left;
        }else if(target>root->element) {
            root=root->right;
        }else {
            return root;
        }
    }
    //如果没找到,就返回NULL
    return NULL;
}
Node findMax(Node root) {
    while(root && root->right) {
        root=root->right;
    }
    return root;
}

四 删除操作

4.1 情况讨论

  • 首先分析一下,删除操作可能出现的情况
    1. 要删除的结点是叶子节点
      在这里插入图片描述

    2. 要删除的结点只有一个孩子结点
      在这里插入图片描述

    3. 要删除的结点有两个孩子结点

      • 为保持二叉查找数的性质,我们需要选择一个孩子补充删除的结点。这里选取其左子树中最大节点上位

在这里插入图片描述

4.2 代码实现

Node deleteValue(Node root,E target) {
    if(root==NULL) return NULL;
    if(target<root->element) {
        root->left= deleteValue(root->left,target);
    }else if(target>root->element) {
        root->right= deleteValue(root->right,target);
    }else {//找到的情况
        //处理结点左右都有孩子的情况
        if(root->left && root->right) {
            //第一步:找到左边最大的值
            Node leftMax= findMax(root->left);
            //第二步:将原来的值替换为其左子树的最大值
            root->element=leftMax->element;
            //第三步:删除最大值【只有一个左孩子或者没孩子】,返回删除结点的根节点
            //同时注意的是:将情况转化为删除只有一个孩子\叶子结点的情况
            //函数会进入下面的情况[else]中进行处理
            root->left= deleteValue(root->left,root->element);
        }else {
            //只要删除这个节点,
            //当这个节点有走孩子或右孩子时,就进行删除孩子结点并修改指针指向;否则,直接删除当前节点即可
            Node temp=root;
            if(root->right) {
                root=root->right;
            }
            else {
                root=root->left;
            }
            free(temp);
        }
    }
    return root;//返回最终的结点
}

五 完整代码

  • 作者在这里列出完整的代码,并不希望诸位直接粘贴复制
  • 希望诸位可以在运行代码调试的过程中,加深对这部分内容的理解。
  • 删除操作部分的代码涉及到递归且情况较为复杂,读者可以运行这儿的代码,认真理解删除的具体实现!

5.1 二叉查找树的结构

18
10
22
7
15
NULL
8
9
NULL

5.2 完整代码内容

//
// Created by HP on 2023/1/10.
//

#include<iostream>
using namespace std;
typedef int E;
typedef struct TreeNode {
    E element;
    struct TreeNode *left;
    struct TreeNode *right;
} *Node;

Node createNode(E element) {
    Node node = (Node) malloc(sizeof(struct TreeNode));
    node->left = node->right = NULL;
    node->element = element;
    return node;
}

Node insert(Node root,E element) {
    if(root) {
        if(element< root->element)
            root->left= insert(root->left,element);
        else if(element>root->element)
            root->right= insert(root->right,element);
    }else {//当节点为空时,说明已经找到插入的位置,创建对应的结点
        root= createNode(element);
    }
    return root;//返回当前结点
}

void inOrder(Node root) {
    if(root==NULL) return ;
    inOrder(root->left);
    cout<<root->element<<" ";
    inOrder(root->right);
}

Node find(Node root,E target) {
    while(root) {
        //如果要找的值比当前值小,就往左边查找
        //如果要找的值比当前值大,就往右边查找
        //否则,就是找到了
        if(target<root->element) {
            root=root->left;
        }else if(target>root->element) {
            root=root->right;
        }else {
            return root;
        }
    }
    //如果没找到,就返回NULL
    return NULL;
}
Node findMax(Node root) {
    while(root && root->right) {
        root=root->right;
    }
    return root;
}
Node deleteValue(Node root,E target) {
    if(root==NULL) return NULL;
    if(target<root->element) {
        root->left= deleteValue(root->left,target);
    }else if(target>root->element) {
        root->right= deleteValue(root->right,target);
    }else {//找到的情况
        //处理结点左右都有孩子的情况
        if(root->left && root->right) {
            //第一步:找到左边最大的值
            Node leftMax= findMax(root->left);
            //第二步:将原来的值替换为其左子树的最大值
            root->element=leftMax->element;
            //第三步:删除最大值【只有一个左孩子或者没孩子】,返回删除结点的根节点
            //同时注意的是:将情况转化为删除只有一个孩子\叶子结点的情况
            //函数会进入下面的情况[else]中进行处理
            root->left= deleteValue(root->left,root->element);
        }else {
            //只要删除这个节点,
            //当这个节点有走孩子或右孩子时,就进行删除孩子结点并修改指针指向;否则,直接删除当前节点即可
            Node temp=root;
            if(root->right) {
                root=root->right;
            }
            else {
                root=root->left;
            }
            free(temp);
        }
    }
    return root;//返回最终的结点
}
int main() {
    Node root= insert(NULL,18);
    insert(root,22);
    insert(root,10);
    insert(root,7);
    insert(root,15);
    insert(root,9);
    insert(root,8);
    inOrder(root);
    cout<<endl;
    Node res=find(root,17);
    if(res)
        cout<<res->element<<endl;
    else
        cout<<"没找到"<<17<<endl;
    Node res1=find(root,9);
    if(res1)
        cout<<res1->element<<endl;
    else
        cout<<"没找到"<<endl;
    cout<<"成功删除:"<<deleteValue(root,10)->element;
    return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/154248.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(十一)devops持续集成开发——jenkins流水线发布一个docker harbor仓库版的前端vue项目

前言 本节内容&#xff0c;我们使用jenkins的流水线功能发布一个docker harbor私服仓库版的前端vue项目&#xff0c;延续前一节的内容&#xff0c;这里需要我们事先安装好一个docker harbor仓库用来存放我们项目的镜像&#xff0c;前端项目依然是通过nginx基础镜像构建&#x…

关于时间复杂度什么是时间复杂度

文章目录简介常见的Big-Oh简介 精确的考虑程序运行时间会使得寸步难行&#xff0c;而且毫无意义&#xff0c;所以可以用一种“概量”的概念来衡量运行时间&#xff0c;称之为“时间复杂度”。 时间复杂度的定义&#xff1a; 在一个完全立项状态下的计算机中&#xff0c;我们定…

【Sql Server】数据库的表变量和临时表的区别,并通过变量表随机生成姓名

作者&#xff1a;小5聊 简介&#xff1a;一只喜欢全栈方向的程序员&#xff0c;欢迎咨询&#xff0c;尽绵薄之力答疑解惑 公众号&#xff1a;有趣小馆&#xff0c;一个有趣的关键词回复互动功能 效果 1、表变量 1.1、表变量基本信息 1&#xff09;表变量本质是一个变量 是SQ…

《啊哈算法》第一章典例+解析+代码

目录 一&#xff0c;计数排序 二&#xff0c;冒泡排序&#xff08;Bubble Sort&#xff09; 三&#xff0c;快速排序&#xff08;Quick Sort&#xff09; 四&#xff0c;桶排序&#xff08;Bucket Sort&#xff09; 五&#xff0c;小哼买书 从无到有掌握最基础的算法 多学…

react hooks 封装一个countDown 倒计时组件

开发技术 react , hooks , ts , taro 需求分析 需要一个可以按天&#xff0c;时&#xff0c;分和秒来进行倒计时的组件。 简单使用 注&#xff1a;主要逻辑请看 useCountDown import CountDown from /components/countDown; import { useEffect, useState } from react; i…

东宝商城项目(二)——flask-script模块、flask-migrate模块和项目日志配置

学习flask-script模块的使用 1、什么是flask-script flask-script是flask的一个扩展模块&#xff0c;Flask-Script的作用是可以通过命令行的形式来操作Flask。 2、安装flask-script pip install flask-script 3、flask-script的使用 例如有这样一种需求&#xff1a; 我们…

李彦宏开年定调“百度式创新”:反馈驱动,坚定技术

今天在百度热搜看到这么一条置顶话题&#xff0c;让我印象深刻&#xff1a;读懂中国经济的信心所在。 站在2023年起点&#xff0c;无论你是阳了、没阳&#xff0c;还是阳康了&#xff0c;之于个人、企业组织&#xff0c;都太需要信心和激励了。 点进去后是一篇来自《人民日报 …

小程序直播加速抢占电商流量先机

临近春节&#xff0c;到了购置年货的时候&#xff0c;相信有不少小伙伴被淘宝、拼多多、抖音等各大平台的直播卖货吸引。近年来&#xff0c;大家逐渐发现视频直播的影响力已经渗透到各行各业&#xff0c;通过直播带来的流量&#xff0c;不少商家赚得盆满钵满。视频直播这块流量…

Autosar MCAL-GPT配置及使用

文章目录前言GPTGptChannelConfigSetGptChannelIdGptChannelModeGptChannelTickFrequencyGptChannelTickValueMaxGptEnableWakeupGptNotificationGptChannelClkSrcRefGptAssignedHwUnitGptConfigurationOfOptApiServicesGptDeinitApiGptEnableDisableNotificationApiGptTimeEla…

系列33 Flow_Model

Introduction 在上一小节中讲到了Latent Variable Model&#xff08;LAM&#xff09;&#xff0c;VAE。其主要思想就是将隐变量扩充为高维连续的分布&#xff0c;来增强模型的表达能力。而LAM模型中的核心困难是计算不出来&#xff0c;因为&#xff0c;而的维度过高算不出来。而…

GAN Step By Step -- Step7 WGAN

GAN Step By Step 心血来潮 GSBS&#xff0c;顾名思义&#xff0c;我希望我自己能够一步一步的学习GAN。GAN 又名 生成对抗网络&#xff0c;是最近几年很热门的一种无监督算法&#xff0c;他能生成出非常逼真的照片&#xff0c;图像甚至视频。GAN是一个图像的全新的领域&#…

2022 OceanBase 年度报告|用技术让海量数据的管理和使用更简单!

尊敬的各位客户、合作伙伴和开发者&#xff1a; 从 2020 年 6 月 1 日 OceanBase 开启商业化至今&#xff0c;我们一起走过了 900 多天。 从 0.5 到 3.x&#xff0c;我们花了近十年时间&#xff0c;而从 3.x 到 4.x 只用了不到两年&#xff0c;这是 OceanBase 和客户、伙伴、…

C++ 开发环境其实挺难搞的(上)

所谓工欲善其事&#xff0c;必先利其器&#xff0c;我将用两节课的内容向大家介绍 C 的开发工具及开发环境的一些知识。 Visual Studio 诞生至今已有 25 年以上的历史&#xff0c;功能强大&#xff0c;用的人非常多&#xff0c;社区的朋友戏称它为宇宙最强大的 IDE&#xff0c…

LeetCode 147. 对链表进行插入排序

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 147. 对链表进行插入排序&#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名称 二、…

ECC原理和RocketChip Cache ECC实现

一、ECC原理说明ECC(Error Correcting Code)全称为错误纠正码&#xff0c;用于对存储器的数据进行完整性检查和纠正&#xff0c;主要用在SRAM、DDR、NAND等存储器设备上。ECC可以对数据进行单比特的纠错和多比特的检错&#xff0c;其原理基于汉明码编码而来。下图是ECC编码的主…

如何通过光耦合器连接继电器

如何通过光耦合器连接继电器 介绍 以下文章介绍如何使用隔离方法或通过光耦合器器件驱动继电器。我们将学习三种方法&#xff0c;第一种方法是将继电器直接连接到光耦合器输出引脚&#xff0c;第二种方法是使用外部PNP晶体管&#xff0c;第三种方法是使用外部NPN晶体管。任何…

二十九、Docker (5)

&#x1f33b;&#x1f33b; 目录一、Maven Docker 插件构建 Docker 镜像1.1 maven Docker 插件构建 Docker 镜像入门1.2 maven Docker 插件构建 Docker 镜像&#xff0c;自定义 DockerFile1.3 maven Docker 插件构建并推送镜像到 Docker 私有仓库二、手动发布部署微服务项目到…

类与对象(三):stactic成员、友元、内部类

类与对象&#xff08;三&#xff09;1. 再谈构造函数1.1 构造函数体赋值1.2 初始化列表1.3 explicit关键字2. static成员2.1 概念特性类中成员变量区别&#xff08;普通变量和static变量&#xff09;:普通成员函数和静态成员函数的区别3. 友元友元函数友元类cout打印自定义类型…

24种代码坏味道和重构手法

最近&#xff0c;小李感觉公司女生们看他的眼神不太对劲了&#xff0c;那种笑容好像是充满慈爱的、姨母般的笑容。 作为一名老实本分的程序员&#xff0c;小李不太习惯这种被人过度关注的感觉&#xff0c;他不知道发生了什么。 小李和小王的关系似乎过于亲密&#xff0c;还经…

ZooKeeper 安装

ZooKeeper 安装 1. 下载安装 1、环境准备 ZooKeeper服务器是用Java创建的&#xff0c;它运行在JVM之上。需要安装JDK 7或更高版本。 2、上传 将下载的ZooKeeper放到/opt/ZooKeeper目录下 #上传zookeeper altp put f:/setup/apache-zookeeper-3.5.6-bin.tar.gz #打开 opt目…