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 节点,避免内存泄漏

相关推荐
鱼很腾apoc10 小时前
【MySQL】第4期 用户管理与 C 语言连接编程详解
linux·服务器·c语言·mysql·connector/c
himobrinehacken12 小时前
位运算揭秘:左右移与符号位陷阱
c语言·c++·算法·安全
水饺编程14 小时前
第2章:Linux 基础,rm 命令
linux·c语言·汇编
夏虫不可与之言冰15 小时前
第三课 点亮 LED 与定时器非阻塞闪烁
c语言·c++·vscode·单片机·嵌入式硬件·物联网
JasmineX-115 小时前
数据结构(笔记)——顺序栈
c语言·数据结构·笔记
2401_8628808215 小时前
嵌入式Linux系统移植与驱动开发完整指南
linux·c语言·驱动开发
我叫洋洋17 小时前
Cadence 16.6 从 OrCAD Capture 原理图到 Allegro PCB 全流程(含 5 个常见坑)
c语言·eda·嘉立创
水饺编程18 小时前
第2章:Linux 基础,关机命令
linux·c语言·汇编
Brilliantwxx19 小时前
【Linux】IO(上)全篇:文件本质、C 接口、系统调用与文件描述符
linux·运维·服务器·c语言
坐看风云变幻19 小时前
《嵌入式工程师的软硬件修仙之路》第 2 期|C 语言筑基:指针与内存模型
c语言·开发语言