二叉搜索树学习记录

1. 什么是二叉搜索树

二叉搜索树(Binary Search Tree,简称 BST)是一种特殊的二叉树,它满足以下性质:

  • 若左子树不为空,则左子树上所有节点的值均小于根节点的值。
  • 若右子树不为空,则右子树上所有节点的值均大于根节点的值。
  • 左右子树本身也分别是二叉搜索树。

基于这一性质,二叉搜索树在理想情况下可以实现 O(log n) 的查找、插入和删除操作,是许多高效数据结构(如 AVL 树、红黑树)的基础。

2. 二叉搜索树的结构定义

在 C 语言中,我们通常使用结构体来定义二叉搜索树的节点。每个节点包含一个数据域和两个指针域,分别指向左孩子和右孩子。

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

// 定义二叉搜索树节点
typedef struct Node {
    int data;               // 数据域
    struct Node *left;      // 左孩子指针
    struct Node *right;     // 右孩子指针
} Node;

// 创建新节点
Node* createNode(int data) {
    Node *newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

3. 二叉搜索树的插入操作

插入操作遵循二叉搜索树的性质:从根节点开始,若待插入的值小于当前节点,则向左子树递归插入;若大于当前节点,则向右子树递归插入;直到找到空位置为止。

c 复制代码
// 插入节点(递归实现)
Node* insert(Node *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);
    }
    // 值已存在,不重复插入(可根据需求处理)

    return root;
}

4. 二叉搜索树的查找操作

查找操作同样利用二叉搜索树的有序性:从根节点开始,若目标值等于当前节点则查找成功;若小于当前节点则向左子树查找;若大于当前节点则向右子树查找。若遇到空节点仍未找到,则说明目标值不存在。

c 复制代码
// 查找节点(递归实现)
Node* search(Node *root, int target) {
    // 空树或找到目标值
    if (root == NULL || root->data == target) {
        return root;
    }

    // 目标值小于当前节点,向左子树查找
    if (target < root->data) {
        return search(root->left, target);
    }
    // 目标值大于当前节点,向右子树查找
    return search(root->right, target);
}

5. 二叉搜索树的删除操作

删除操作是二叉搜索树中最复杂的操作,需要分三种情况讨论:

  • 情况一:待删除节点是叶子节点,直接删除即可。
  • 情况二:待删除节点只有一个孩子,用其孩子节点替代它。
  • 情况三:待删除节点有两个孩子,通常用其左子树中的最大节点或右子树中的最小节点来替代它。
c 复制代码
// 查找以 root 为根的最小节点
Node* findMin(Node *root) {
    while (root->left != NULL) {
        root = root->left;
    }
    return root;
}

// 删除节点(递归实现)
Node* deleteNode(Node *root, int target) {
    // 空树,直接返回
    if (root == NULL) {
        return root;
    }

    // 目标值小于当前节点,向左子树递归删除
    if (target < root->data) {
        root->left = deleteNode(root->left, target);
    }
    // 目标值大于当前节点,向右子树递归删除
    else if (target > root->data) {
        root->right = deleteNode(root->right, target);
    }
    // 找到待删除节点
    else {
        // 情况一:叶子节点或只有一个孩子
        if (root->left == NULL) {
            Node *temp = root->right;
            free(root);
            return temp;
        }
        else if (root->right == NULL) {
            Node *temp = root->left;
            free(root);
            return temp;
        }

        // 情况三:有两个孩子,用右子树最小节点替代
        Node *temp = findMin(root->right);
        root->data = temp->data;
        root->right = deleteNode(root->right, temp->data);
    }

    return root;
}

6. 二叉搜索树的遍历

二叉搜索树支持三种经典的深度优先遍历方式:前序遍历、中序遍历和后序遍历。其中,中序遍历二叉搜索树可以得到一个升序序列,这是二叉搜索树最重要的特性之一。

c 复制代码
// 前序遍历:根 -> 左 -> 右
void preorder(Node *root) {
    if (root == NULL) return;
    printf("%d ", root->data);
    preorder(root->left);
    preorder(root->right);
}

// 中序遍历:左 -> 根 -> 右(结果为升序)
void inorder(Node *root) {
    if (root == NULL) return;
    inorder(root->left);
    printf("%d ", root->data);
    inorder(root->right);
}

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

7. 完整示例与测试

下面给出一个完整的 C 语言程序,演示二叉搜索树的创建、插入、查找、删除和遍历操作。

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

typedef struct Node {
    int data;
    struct Node *left;
    struct Node *right;
} Node;

Node* createNode(int data) {
    Node *newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

Node* insert(Node *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);
    }
    return root;
}

Node* search(Node *root, int target) {
    if (root == NULL || root->data == target) {
        return root;
    }
    if (target < root->data) {
        return search(root->left, target);
    }
    return search(root->right, target);
}

Node* findMin(Node *root) {
    while (root->left != NULL) {
        root = root->left;
    }
    return root;
}

Node* deleteNode(Node *root, int target) {
    if (root == NULL) {
        return root;
    }
    if (target < root->data) {
        root->left = deleteNode(root->left, target);
    } else if (target > root->data) {
        root->right = deleteNode(root->right, target);
    } else {
        if (root->left == NULL) {
            Node *temp = root->right;
            free(root);
            return temp;
        } else if (root->right == NULL) {
            Node *temp = root->left;
            free(root);
            return temp;
        }
        Node *temp = findMin(root->right);
        root->data = temp->data;
        root->right = deleteNode(root->right, temp->data);
    }
    return root;
}

void inorder(Node *root) {
    if (root == NULL) return;
    inorder(root->left);
    printf("%d ", root->data);
    inorder(root->right);
}

void freeTree(Node *root) {
    if (root == NULL) return;
    freeTree(root->left);
    freeTree(root->right);
    free(root);
}

int main() {
    Node *root = NULL;
    int values[] = {50, 30, 70, 20, 40, 60, 80};
    int n = sizeof(values) / sizeof(values[0]);

    // 构建二叉搜索树
    for (int i = 0; i < n; i++) {
        root = insert(root, values[i]);
    }

    printf("中序遍历结果:");
    inorder(root);
    printf("\n");

    // 查找操作
    Node *found = search(root, 40);
    if (found != NULL) {
        printf("查找 40:找到\n");
    } else {
        printf("查找 40:未找到\n");
    }

    // 删除操作
    root = deleteNode(root, 30);
    printf("删除 30 后中序遍历:");
    inorder(root);
    printf("\n");

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

8. 复杂度分析与总结

二叉搜索树的性能取决于树的高度:

操作 平均时间复杂度 最坏时间复杂度
查找 O(log n) O(n)
插入 O(log n) O(n)
删除 O(log n) O(n)

当二叉搜索树退化为一条链时(例如按升序插入数据),其性能会退化为 O(n)。为了解决这一问题,后续发展出了 AVL 树、红黑树等自平衡二叉搜索树,它们通过旋转操作保持树的高度平衡,从而保证操作始终在 O(log n) 时间内完成。

二叉搜索树是数据结构学习中的重要基石,理解其原理和实现,对于掌握更复杂的平衡树结构大有裨益。

相关推荐
手写码匠1 小时前
DeepSeek 函数调用实战:从零搭建一个会“动手“的 AI 助手
人工智能·深度学习·算法·aigc
蒸蒸yyyyzwd2 小时前
机试和Redis学习笔记
c++·笔记·面试
果壳science2 小时前
《自然本源》如何实现狭义相对论的代数化
笔记·算法·几何学
suhan2 小时前
【数据结构】二、线性表:2.单链表的定义与实现(带头结点与不带头结点的区别)
数据结构
1314lay_10073 小时前
C#调用Sql Server存储过程,并且使用传入表结构
数据库·经验分享·笔记·sqlserver·c#
302wanger3 小时前
分享一个工作习惯:整点看即时消息
算法
志尊宝4 小时前
Vue3 零基础每日笔记(015):条件渲染 v-if 与 v-show——控制 DOM 生死还是控制显隐
前端·vue.js·笔记
洋不写bug5 小时前
排序(一)基础排序,插入|希尔|冒泡|直接选择排序详解
java·算法·排序算法·插入排序·冒泡排序·希尔排序·直接选择排序
牧羊人.3335 小时前
动手学深度学习 03 | 卷积神经网络实现手写数字识别
人工智能·深度学习·神经网络·算法·cnn