C语言 二叉搜索树(BST)超详细完整实现(插入+查找+删除+三种遍历+内存释放)

一、前言

二叉搜索树(BST,Binary Search Tree)是数据结构中最基础、面试最常考的树形结构。

本文使用 C语言 + 递归 完整实现二叉搜索树所有核心功能:

  • 节点创建

  • 节点插入

  • 节点查找

  • 最小值查找

  • 节点删除(三大删除情况全覆盖)

  • 前序/中序/后序遍历

  • 整树内存释放

代码可直接编译运行,注释极其详细,适合新手学习、期末作业、面试复习。

二、二叉搜索树核心性质

二叉搜索树必须满足以下规则:

  1. 左子树所有节点值 小于 根节点值

  2. 右子树所有节点值 大于 根节点值

  3. 左右子树也必须是二叉搜索树

  4. BST 不允许存储重复数据

  5. 中序遍历结果一定是升序序列(BST 最重要特性)

三、完整可运行代码

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

// 定义二叉搜索树的节点结构
typedef struct TreeNode {
    int data;                   // 节点存储的数据
    struct TreeNode* left;      // 左子节点指针
    struct TreeNode* right;     // 右子节点指针
} TreeNode;

// 创建新节点
TreeNode* createNode(int data) {
    TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
    if (newNode == NULL) {
        printf("内存分配失败\n");
        exit(EXIT_FAILURE);
    }
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

// 递归插入节点
TreeNode* insert(TreeNode* root, int data) {
    // 空树位置,创建新节点
    if (root == NULL) {
        return createNode(data);
    }

    // 比根小插左边
    if (data < root->data) {
        root->left = insert(root->left, data);
    }
    // 比根大插右边
    else if (data > root->data) {
        root->right = insert(root->right, data);
    }
    // 相等不处理(BST无重复节点)
    return root;
}

// 递归查找节点
TreeNode* search(TreeNode* root, int key) {
    // 找到节点或遍历到空节点,返回
    if (root == NULL || root->data == key) {
        return root;
    }

    // 小于当前节点,往左找
    if (key < root->data) {
        return search(root->left, key);
    }
    // 大于当前节点,往右找
    return search(root->right, key);
}

// 查找以root为根的树的最小值(最左节点)
TreeNode* findMin(TreeNode* root) {
    while (root->left != NULL) {
        root = root->left;
    }
    return root;
}

// 删除指定节点
TreeNode* deleteNode(TreeNode* root, int key) {
    // 空树直接返回
    if (root == NULL) {
        return root;
    }

    // 递归寻找待删除节点
    if (key < root->data) {
        root->left = deleteNode(root->left, key);
    } else if (key > root->data) {
        root->right = deleteNode(root->right, key);
    } 
    // 找到需要删除的节点
    else {
        // 情况1:无左孩子 / 无孩子
        if (root->left == NULL) {
            TreeNode* temp = root->right;
            free(root);
            return temp;
        }
        // 情况2:无右孩子
        else if (root->right == NULL) {
            TreeNode* temp = root->left;
            free(root);
            return temp;
        }

        // 情况3:左右孩子都存在
        // 用右子树最小值替换当前节点,再删除最小值节点
        TreeNode* temp = findMin(root->right);
        root->data = temp->data;
        root->right = deleteNode(root->right, temp->data);
    }
    return root;
}

// 中序遍历:左-根-右(升序)
void inorderTraversal(TreeNode* root) {
    if (root != NULL) {
        inorderTraversal(root->left);
        printf("%d ", root->data);
        inorderTraversal(root->right);
    }
}

// 前序遍历:根-左-右
void preorderTraversal(TreeNode* root) {
    if (root != NULL) {
        printf("%d ", root->data);
        preorderTraversal(root->left);
        preorderTraversal(root->right);
    }
}

// 后序遍历:左-右-根
void postorderTraversal(TreeNode* root) {
    if (root != NULL) {
        postorderTraversal(root->left);
        postorderTraversal(root->right);
        printf("%d ", root->data);
    }
}

// 递归销毁整棵树,释放内存
void destroyTree(TreeNode* root) {
    if (root != NULL) {
        destroyTree(root->left);
        destroyTree(root->right);
        free(root);
    }
}

// 主函数测试
int main() {
    TreeNode* root = NULL;

    // 插入测试数据
    root = insert(root, 50);
    insert(root, 30);
    insert(root, 20);
    insert(root, 40);
    insert(root, 70);
    insert(root, 60);
    insert(root, 80);

    // 三种遍历输出
    printf("中序遍历(升序): ");
    inorderTraversal(root);
    printf("\n");

    printf("前序遍历: ");
    preorderTraversal(root);
    printf("\n");

    printf("后序遍历: ");
    postorderTraversal(root);
    printf("\n");

    // 查找测试
    TreeNode* found = search(root, 60);
    if (found) {
        printf("成功找到节点: %d\n", found->data);
    } else {
        printf("未找到该节点\n");
    }

    // 删除节点测试
    root = deleteNode(root, 30);
    printf("删除节点30后【中序遍历】: ");
    inorderTraversal(root);
    printf("\n");

    // 释放内存
    destroyTree(root);
    return 0;
}

四、运行结果

编译运行后输出如下:

cpp 复制代码
中序遍历(升序): 20 30 40 50 60 70 80 
前序遍历: 50 30 20 40 70 60 80 
后序遍历: 20 40 30 60 80 70 50 
成功找到节点: 60
删除节点30后【中序遍历】: 20 40 50 60 70 80 

五、核心函数原理详解

1. 节点插入

递归找空位置,小的往左、大的往右,重复数据直接忽略。

2. 节点查找

利用 BST 有序性,每次减半搜索范围,效率远高于普通遍历。

3. 节点删除(重难点)

BST 删除分三种情况:

  • 无孩子:直接释放当前节点

  • 单个孩子:用孩子顶替当前节点

  • 两个孩子 :右子树最小值替换 + 删除最小值节点(本文标准写法)

4. 三种遍历区别

  • 前序:根左右(用于复制树、获取结构)

  • 中序:左根右(BST 升序输出,最常用)

  • 后序:左右根(用于销毁树、计算树高度)

六、知识点总结(面试/考试必备)

  • 二叉搜索树的最大优势:查找、插入、删除效率高,理想时间复杂度 O(logn)

  • BST 中序遍历一定是有序数组

  • 删除双孩子节点必须用后继/前驱节点替换,保证树结构不变

  • 必须手动 free 节点,避免内存泄漏

相关推荐
神仙别闹3 小时前
基于C语言实现 TINY+ 编译器
c语言·开发语言·数据库
共享家95274 小时前
C语言-408
c语言·开发语言
ACP广源盛139246256736 小时前
接口适配方案选型|GSV6125@ACP HDMI2.0 转 Type‑C/DP1.4 桥接芯片工程评估
大数据·c语言·开发语言·硬件架构·硬件工程·国产芯片
芯祥联9 小时前
STM32 上跑 MQTT 怎么选?嵌入式 MQTT 客户端 C 语言实现对比
c语言·stm32·单片机·嵌入式硬件·网络协议·开源·broker
Escalating_xu17 小时前
【C 语言】深入理解指针(1·下):指针运算、野指针、assert 与传址实战
java·c语言·开发语言
阿钱真强道18 小时前
20 嵌入式操作系统 | ubus:把自己的程序状态暴露出去
c语言·进程通信·ubus·ubusd·blobmsg
小小龙学IT20 小时前
三菱 PLC MC 协议(SLMP / QnA 兼容 3E 帧)深度解析:从帧结构到 C++/Go 双语言采集实战
c语言·c++·golang
Logic10121 小时前
C语言/数据结构位运算题解:异或XOR找出游戏速通中的“独特初始能量值“——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
Chen—LSN1 天前
C语言——⽂件操作(1)
c语言·开发语言·c++·经验分享·笔记·算法·链表