数据结构---二叉搜索树的实现

数据结构---二叉搜索树的实现

一. 内容概述

二叉搜索树,又叫二叉排序/查找树。特点是普通二叉树加一定约束后形成的具有特殊性质的二叉树,其规则约束有以下:

txt 复制代码
1.如果它的左⼦树不空,则左⼦树上所有结点的值均⼩于它的根结点的值。
2.如果它的右⼦树不空,则右⼦树上所有结点的值均⼤于它的根结点的值。
3.它的左、右树⼜分为二叉排序树。

对于一颗满足上述约束的树,它的中序遍历结果就是一个从小到大排好的序列。其次,二叉搜索树的优势在于当要查找一个树中存在的数据时,其查找速度等同于二分查找(最坏情况所需时间为遍历该树的高度),无需遍历整棵树,只要通过该树性质不断比较递归就可以得到答案。以下会介绍二叉搜索树的具体插入删除等操作的具体实现。

二. 代码实现

1.结构定义

c 复制代码
typedef int Element;
// 定义二叉搜索树的节点结构
typedef struct _bs_node {
    Element data;
    struct _bs_node *left;
    struct _bs_node *right;
} BSNode;
// 定义二叉搜索树的头节点
typedef struct {
    BSNode *root;
    int count;
    const char *name;
} BSTree;

2.创建树/节点

c 复制代码
BSTree* createBSTree(const char* name)
{
    BSTree* tree = malloc(sizeof(BSTree));
    if (tree == NULL)
    {
       return NULL;
    }
    tree->root = NULL;
    tree->name = name;
    tree->count = 0;
    return tree;
}

static BSNode *createBSNode(Element e)
{
    BSNode *node = malloc(sizeof(BSNode));
    if (node == NULL)
    {
       return NULL;
    }
    node->data = e;
    node->left = node->right = NULL;
    return node;
}

3.插入操作(递归)

c 复制代码
static BSNode* insertBSNode(BSTree *tree, BSNode *node, Element e)
{
    if (node == NULL)//判空
    {
       tree->count++;
       return createBSNode(e);
    }
    if (e < node->data)//插入数据比当前节点存储数据小
    {
       node->left = insertBSNode(tree,node->left,e);//递归直到下一个节点位置为NULL,创建新节点并插入当前位置
    }
    else if (e>node->data)//同理
    {
       node->right = insertBSNode(tree,node->right,e);
    }
    return node;
}

void insertBSTree(BSTree* tree, Element e)
{
    tree->root = insertBSNode(tree,tree->root,e);
}

4.插入操作(非递归)*

非递归主要依靠双指针(一个指向当前检索节点,一个用来记录另一个指针的前一步位置),防止指针指空无法追踪位置。

c 复制代码
void insertBSTree_2(BSTree* tree, Element e)
{
    BSNode *cur = tree->root;//首先定义一个指针指向根节点
    BSNode *pre = NULL;//第二个指针初始定义指空
    while (cur)//若cur指针未找到e应插入位置或e在树中已存在,不断循环
    {
       pre = cur;//pre在cur移位前记录其位置
       if (e < cur->data) cur=cur->left;
       else if (e > cur->data) cur=cur->right;
       else return;//若e已存在退出插入操作
    }
    BSNode *newNode = createBSNode(e);//cur已经到达e应插入位置且e之前并不存在
    if (pre)//pre不为空(若空说明root为NULL)
    {
       if (e < pre->data) pre->left = newNode;//根据e与pre大小判断确定位置并进行插入操作
       else if (e > pre->data) pre->right = newNode;
    }
    else//root为NULL,e为首个插入的节点
    {
       tree->root = newNode;
    }
    tree->count++;
}

5.删除节点操作

c 复制代码
static BSNode *maxValueBSNode(BSNode *node)//从node节点开始寻找最大节点
{
    while (node && node->right)
    {
       node = node->right;
    }
    return node;
}

static BSNode* deleteBSNode(BSTree* tree, BSNode *node, Element e)//从node开始寻找e
{
    if (node == NULL)//非空判断或递归终止条件
    {
       return NULL;
    }
    if (e < node->data)//递归遍历寻找e在树中位置
    {
       node->left = deleteBSNode(tree,node->left,e);
    }
    else if (e > node->data)
    {
       node->right = deleteBSNode(tree,node->right,e);
    }
    else//找到e的位置
    {
       BSNode *temp;//定义temp记录待删除节点
       if (node->left == NULL)//首先判断左节点为空,说明node节点度为1或0
       {
          temp = node->right;//node左节点为空,temp记录右节点防止丢失数据
          free(node);//将待删除结点释放
          tree->count--;//计数器减一
          return temp;//将保留数据接回原树
       }
       if (node->right == NULL)//左右节点皆空,重复上述操作,相当于用NULL节点替换待删除节点,与上步本质一样
       {
          temp = node->left;
          free(node);
          tree->count--;
          return temp;
       }
       //到此说明待删节点度为2,为保证搜索树性质不变,要将该节点中序遍历的前驱或后继节点替换待删节点,保证性质不变
       temp = maxValueBSNode(node->left);//示例代码用前驱节点示例,即待删节点左子树最大值
       node->data = temp->data;//将待删节点数据替为前驱节点数据
       node->left = deleteBSNode(tree,node->left,node->data);//寻找到用来替换的节点删除(度一定为1或0)
    }
    return node;//将修改后节点返回
}

void deleteBSTree(BSTree* tree, Element e)
{
    if (tree)
    {
       tree->root = deleteBSNode(tree,tree->root,e);
    }
}

6.遍历操作

c 复制代码
void visitBSNode(const BSNode* node)//访问节点函数,此处示例为将节点内数据输出
{
    printf("\t%d",node->data);
}

static void inOrderBSNode(const BSNode *node)//中序遍历
{
    if (node)
    {
       inOrderBSNode(node->left);
       visitBSNode(node);
       inOrderBSNode(node->right);
    }
}

void inOrderBSTree(const BSTree* tree)
{
    printf("[%s]Tree: ", tree->name);
    inOrderBSNode(tree->root);
    printf("\n");
}

7.求树的高度

c 复制代码
static int heightBSNode(const BSNode* node)
{
    if (node ==NULL)//递归终止条件
    {
       return 0;
    }
    int leftHeight = heightBSNode(node->left);//不断递归将子树高度传递回来
    int rightHeight = heightBSNode(node->right);

    if (leftHeight > rightHeight)//树的高度由更高的子树高度决定,总高度为子树高度+1
       return leftHeight + 1;
    else  return rightHeight + 1;
}

int heightBSTree(const BSTree* tree)
{
    return heightBSNode(tree->root);
}

8.查找操作

c 复制代码
//利用二叉搜索树进行查值操作
BSNode* searchBSTree(const BSTree* tree, Element e)
{
    BSNode* node = tree->root;//从根节点开始寻找
    while (node)
    {
       if (e < node->data)
       {
          node = node->left;
       }
       else if (e > node->data)
       {
          node = node->right;
       }
       else
       {
          return node;
       }
    }
    return NULL;
}

三. 内容总结

二叉搜索树的实现大部分比较容易理解,难点主要有树中度为2的节点删除操作,具体实现不能简单直接释放,而是换一个思路去选择一个前驱或者后继节点用来替换到待删除结点位置,然后将用来替换的节点找到并删除是该操作的具体实现逻辑,此外该树的实现没有其他难以理解的点。

相关推荐
pc0793几秒前
带网络接口的打印机和USB接口打印机的共享区别
运维·服务器·网络
上弦月-编程2 分钟前
【C语言】函数栈帧的创建与销毁(底层原理)
c语言·开发语言
MATLAB代码顾问2 分钟前
混合粒子群-模拟退火算法(HPSO-SA)求解作业车间调度问题——附MATLAB代码
算法·matlab·模拟退火算法
汤愈韬3 分钟前
Full Cone NAT、行为模式
网络·网络协议·网络安全·security
Agent手记4 分钟前
首件检验流程繁琐,耗时久还容易出现合规漏洞怎么办?——基于实在Agent的AI+超自动化全流程闭环实战
网络·人工智能·ai·自动化
Felven7 分钟前
C. Prefix Min and Suffix Max
算法
加农炮手Jinx7 分钟前
LeetCode 26. Remove Duplicates from Sorted Array 题解
算法·leetcode·力扣
加农炮手Jinx7 分钟前
LeetCode 88. Merge Sorted Array 题解
算法·leetcode·力扣
Hhy_11078 分钟前
【从零开始学习数据结构 ④】:栈 ——后进先出的艺术
c语言·数据结构·学习·visual studio
格林威8 分钟前
线阵工业相机:如何计算线阵相机的行频(Line Rate)?公式+实例
开发语言·人工智能·数码相机·算法·计算机视觉·工业相机·线阵相机