C语言实现二叉搜索树BST

news2024/11/16 13:52:40

文章目录

    • 初始化
    • 搜索节点
    • 删除节点

二叉搜索树(Binary Search Tree, BST)要求父节点大于等于其左子节点,而小于等于其右子节点,这样递归类推,相当于父节点大于等于其左侧的所有节点而小于等于右侧的所有节点,如下图所示

根据BST的规则,搜索时只需比较父子节点,故而最坏的情况也无非是查询到叶节点,此时比较次数为树的高度。作为二叉树,元素个数为 n n n,则树高不大于 log ⁡ 2 n \log_2n log2n

初始化

在BST中,任一非根非叶的元素,需要有三个指针,分别指向父节点、左子节点和右子节点。而根节点没有父节点,叶节点没有子节点。C语言实现为

//bst.c
typedef struct TREENODE
{
    struct TREENODE *father;
    struct TREENODE *left;
    struct TREENODE *right;
    int value;
}tNode;

生成BST的第一步就是建立一个根节点,然后不断插入新节点。

插入新节点时,要和当前节点作比较,若新节点大于当前节点,则以其右子节点作为当前节点,继续比较;否则比较左子节点,直到当前节点为叶节点,正好可以把新节点插进来。

void insertNode(tNode* root, int val){
    tNode* new = (tNode*)malloc(sizeof(tNode));
    new->value=val;
    new->left=NULL;
    new->right=NULL;
    while (TRUE){
        if (root->value<val) 
            // 若右子节点存在,则将其置为root,并继续比较
            if(root->right!=NULL)
                root=root->right;
            else{
                //若右子节点不存在,则新节点成为其右子节点
                new->father = root;
                root->right = new;
                return;         //赋值之后函数结束
            }
        else                    //左边的操作与右边相同
            if (root->left!=NULL)
                root=root->left;
            else{
                new->father=root;
                root->left=new;
                return;
            }
    }
}

接下来,检验一下BST是否正确,把所有节点打印出来

//打印二叉搜索树,输入为节点和节点序
void printBST(tNode* root,int start, int isLeft){
    if(isLeft == -1)
        printf("root node is %d\n", root->value);
    else
        printf("the %dth node is %d as %s\n",
            start,root->value,
            isLeft ? "left" : "right");
    //如果当前节有子节点,则继续打印这个子节点和节点序
    if (root->left!=NULL)
        printBST(root->left,start+1, 1);
    if (root->right!=NULL)
        printBST(root->right,start+1, 0);
}

最终在主函数中验证

tNode* initBST(){
    tNode* root;
    root->left=NULL;
    root->right=NULL;
    root->value=10;     //以上初始化根节点

    int init[10]={1,11,5,12,2,4,19,11,8,7};
    for (int i = 0; i < 10; i++)
        insertNode(root,init[i]);

    return root;
}

void testA(){
    tNode* root = initBST();    
    printBST(root,0,-1);
}

int main(){
    testA();
    return 0;
}

其结果为

> gcc .\cTrees.c
> .\a.exe
root node is 10
the 1th node is 1 as left   //第一代节点,10的左子节点
the 2th node is 5 as right  //第二代节点,1的右子节点
the 3th node is 2 as left   //第三代节点,5的左子节点
the 4th node is 4 as right  //第四代节点,2的右子节点
the 3th node is 8 as right  //第三代节点,5的右子节点
the 4th node is 7 as left   //第四代节点,8的左子节点
the 1th node is 11 as right //第一代节点,10的右子节点
the 2th node is 11 as left  //第二代节点,11的左子节点
the 2th node is 12 as right //第二代节点,11的右子节点
the 3th node is 19 as right //第三代节点,12的右子节点
>

生成的树为

搜索节点

BST的结构特征,主要目的是便于搜索,接下来就为其添加新搜索功能。其中,搜索与插入如出一辙,而且更加简单,只需返回这个值的指针。同时,循环判定也变为while(root->value!=val)

//通过节点的值搜索节点地址,root为根节点
tNode* searchBST(tNode* root, int val){
    while (root->value!=val)
    {
        if (root->value<val && root->right!=NULL)
            root=root->right;
        else if (root->value>=val && root->left!=NULL)
            root=root->left;
        else
            return FALSE;
    }
    return root;
}

除了搜索到某个节点之外,有时还需要搜索BST的最大值或者最小值,其真正含义就是找到其最右端或者最左端的节点。

int findMin(tNode* root){
    if(root->left != NULL)
        return findMin(root->left);
    return root->value;
}

int findMax(tNode* root){
    if(root->right != NULL)
        return findMax(root->left);
    return root->value;
}

删除节点

删除节点相对复杂,毕竟涉及到其父子节点和兄弟节点之间的大小关系。

记将要被删除的节点为 D D D,其父节点为 D F D_F DF。若 D D D无子,那当然皆大欢喜,只需将 D F D_F DF指向 D D D的指针变成NULL;如果只有一个子节点 D L D_L DL,也并不麻烦,只需将 D F D_F DF指向 D D D的指针改为指向 D L D_L DL

如果 D D D的左子节点为 D L D_L DL,右子节点为 D R D_R DR,那问题就比较麻烦了,需要找到左子节点中的最大值,或者右子节点中的最小值 m m m,然后把这个 m m m对应的节点删除。

//删除节点的值,root为根节点,delNode为待删除节点
void deleteNode(tNode* delNode){
    tNode* pNode;
    // 左右子节点均为NULL
    if(delNode->left==NULL&&delNode->right==NULL){
        pNode = delNode->father;        //当前节点的父节点
        if(delNode->value>pNode->value)
            pNode->right=NULL;
        else
            pNode->left=NULL;
    }
    // 左右子节点均不为NULL
    else if(delNode->left!=NULL&&delNode->right!=NULL){
        pNode = delNode->right;
        //替换为右子节点中的最小值
        delNode->value = findMin(pNode);  
        // 删除右子节点中的最小值
        while(pNode->left != NULL)
            pNode = pNode->left;
        pNode->father->left = NULL;
    }
    else{
        pNode = (delNode->left==NULL) \
            ? delNode->right : delNode->left;
        delNode->value = pNode->value;
        delNode->right = pNode->right;
        delNode->left = pNode->left;
    }
}

验证一下

void testB(){
    tNode* root = initBST();    
    tNode* sNode=searchBST(root,5);
    deleteNode(sNode);
    printBST(root,0);
}

int main(){
    testB();
    return 0;
}

结果为

PS E:\Code\AlgC> gcc .\cTrees.c
PS E:\Code\AlgC> .\a.exe       
root node is 10
the 1th node is 1 as left
the 2th node is 7 as right
the 3th node is 2 as left
the 4th node is 4 as right
the 3th node is 8 as right
the 1th node is 11 as right
the 2th node is 11 as left
the 2th node is 12 as right
the 3th node is 19 as right

示意图为

在这里插入图片描述

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

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

相关文章

闭包治愈“全局变量恐惧症”,利用闭包实现JavaScript私有变量

文章目录 I. 介绍对闭包的定义和概述为什么理解闭包很重要 II. 函数与作用域函数的作用域和生命周期闭包是如何利用函数的作用域的 III. 闭包的实现闭包的实现方式如何创建闭包闭包的应用场景 IV. 闭包的优缺点闭包的优点数据的封装可以实现高阶函数 闭包的缺点内存占用对程序员…

MedLSAM:定位和分割任何3D医学图像模型

文章目录 MedLSAM: Localize and Segment Anything Model for 3D Medical Images摘要本文方法模型学习过程模型推理过程 实验结果 MedLSAM: Localize and Segment Anything Model for 3D Medical Images 摘要 SAM (Segment Anything Model)是近年来出现的一种具有开创性的图像…

【Linux】gcc/g++ 调试学习记录

这是目录 gcc编译选项二、实战1、编译加上 CFLAGS -ggdb三级目录 gcc编译选项 1、-g 编译debug debugging 选项&#xff1a;-g gcc -g手册: 点击这里 -g一共分为4个等级&#xff1a;-g、-g0、-g1、-g3 其中g和g0是一个性质&#xff0c;不打开调试信息&#xff0c;g3保留所有信…

react-native-camera插件的使用,以及实现人脸识别

一、git地址和环境版本 &#xff08;1&#xff09;Git地址&#xff1a;https://github.com/react-native-camera/react-native-camera/tree/master &#xff08;2&#xff09;node版本&#xff1a;14 &#xff08;3&#xff09;react-native版本&#xff1a;0.72 二、环境配…

Linux操作系统——第五章 进程信号

目录 信号概念 用kill -l命令可以察看系统定义的信号列表 信号处理常见方式概览 产生信号 1. 通过终端按键产生信号 2. 调用系统函数向进程发信号 3. 由软件条件产生信号 4. 硬件异常产生信号 阻塞信号 1. 信号其他相关常见概念 2. 在内核中的表示 3. sigset_t 4.…

spring.aop 随笔4 如何借助jdk代理类实现aop

0. 下了有一个月的雨&#xff0c;这对鼻炎来说来吗&#xff1f;不好 其实这也算6月份的博客&#xff0c;之前一直疏于整理 本文仅关注jdk代理所实现的spring.aop下&#xff0c;两者的关系完整的aop源码走读请移步相关 spring.aop 的其他随笔 1. 反编译追踪源码 1.1 jdk代理类…

BPMN2.0规范简介

1 概述 BPMN(Business Process Model & Notation)&#xff0c;中文名为业务流程模型与符号。BPMN2.0是OMG(Object Management Group&#xff0c;对象管理组织)制定的&#xff0c;其主要目的是既给用户提供一套简单的、容易理解的机制&#xff0c;以便用户创建流程模型&…

项目性能优化-内存泄漏检测与修改

最近终于有空优化一波项目的性能了&#xff0c;第一波借助Android Studio自带的Profiler工具检测内存泄漏。 第一步、创建Profiler的SESSIONS 第二步、进入MEMORY内存监控 右侧带有绿色原点的就是此时运行的Profiler的SESSION,点击右侧MEMORY进入内存监控的详情模块 第三步…

缓存三击-缓存穿透、缓存雪崩、缓存击穿

缓存三击-缓存穿透、缓存雪崩、缓存击穿 ⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记链接&#x1f449;https://github.com/A-BigTree/Code_Learning ⭐⭐⭐⭐⭐⭐ Spring专栏&#x1f449;https://blog.csdn.net/weixin_53580595/category_12279…

【产品设计】掌握“4+X”模型,从0到1构建B端产品

“4X”模型是什么 4个阶段&#xff1a;规划阶段&#xff0c;设计阶段&#xff0c;实现阶段&#xff0c;迭代阶段 X:项目管理&#xff0c;数据分析&#xff0c;产品运营 1、规划阶段 这是一个产品的开始&#xff0c;它决定了产品的设计方向和基调。主要包括用户分析、市场分…

爬虫入门指南(4): 使用Selenium和API爬取动态网页的最佳方法

文章目录 动态网页爬取静态网页与动态网页的区别使用Selenium实现动态网页爬取Selenium 的语法及介绍Selenium简介安装和配置创建WebDriver对象页面交互操作 元素定位 等待机制页面切换和弹窗处理截图和页面信息获取关闭WebDriver对象 使用API获取动态数据未完待续.... 动态网页…

JVM-垃圾回收-基础知识

基础知识 什么是垃圾 简单说就是没有被任何引用指向的对象就是垃圾。后面会有详细说明。 和C的区别 java&#xff1a;GC处理垃圾&#xff0c;开发效率高&#xff0c;执行效率低 C&#xff1a;手工处理垃圾&#xff0c;如果忘记回收&#xff0c;会导致内存泄漏问题。如果回…

Linux Mint 21.2“Victoria”Beta 发布

导读近日消息&#xff0c;Beta 版 Linux Mint 21.2 “Victoria” 于今天发布&#xff0c;用户可以访问官网下载镜像。 Linux Mint 21.2 代号 “Victoria” &#xff0c;基于 Canonical 长期支持的 Ubuntu 22.04 LTS&#xff08;Jammy Jellyfish&#xff09;操作系统&#xff0…

2023年第三届工业自动化、机器人与控制工程国际会议

会议简介 Brief Introduction 2023年第三届工业自动化、机器人与控制工程国际会议&#xff08;IARCE 2023&#xff09; 会议时间&#xff1a;2023年10月27 -30日 召开地点&#xff1a;中国成都 大会官网&#xff1a;www.iarce.org 2023年第三届工业自动化、机器人与控制工程国际…

JAVA http

javahttp 请求数据格式servletservlet生命周期servletrequest获取请求数据解决乱码response相应字符&字节数据 请求数据格式 servlet servlet生命周期 servlet request获取请求数据 解决乱码 response相应字符&字节数据 response.setHeader("content-type",…

A. Portal(dp优化枚举)

Problem - 1580A - Codeforces CQXYM发现了一个大小为nm的矩形。矩形由n行m列的方块组成&#xff0c;每个方块可以是黑曜石方块或空方块。CQXYM可以通过一次操作将黑曜石方块变为空方块&#xff0c;或将空方块变为黑曜石方块。 一个大小为ab的矩形M被称为传送门&#xff0c;当…

【Linux】程序员的基本素养学习

这是目录 写在前面一、内存管理1、分段2、分页 二、线程管理三、静态库1、编译1.1、预处理1.2、编译1.3、汇编1.4、链接2、编译器3、目标文件**.text****.data****.bss****__attribute__** 3.1、符号3.2、兼容C语言 -- extern C4、链接 -- ld 写在前面 本文记录自己的学习生涯…

五.组合数据类型

目录 1、数组类型 声明数组 初始化数组 数组赋值 访问数组元素 2、切片类型 1、定义切片 2、切片初始化 3、访问 4、空(nil)切片 5、切片的增删改查操作&#xff1a; 3、指针类型 1、什么是指针 2、如何使用指针、指针使用流程&#xff1a; 3、Go 空指针 4、指…

chatgpt赋能python:如何将Python打包-一个SEO优化指南

如何将Python打包 - 一个SEO优化指南 作为一名拥有10年Python编程经验的工程师&#xff0c;我意识到很多Python开发者面临一个共同的问题&#xff1a;如何将他们的Python项目打包并发布到PyPI上&#xff1f;打包一个Python项目不仅可以让您的代码更加组织化&#xff0c;也可以…

如何拆分PDF?拆分PDF软件分享!​

那么如何拆分PDF&#xff1f;PDF是一种流行的电子文档格式&#xff0c;它可以在不同的操作系统和设备上进行查看和共享&#xff0c;而不会因为不同的软件或硬件而出现兼容性问题。同时&#xff0c;在使用的过程中&#xff0c;PDF拆分PDF文件是一个比较常见的需求&#xff0c;它…