二叉搜索树学习记录

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) 时间内完成。

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

相关推荐
武子康2 小时前
驾驶轨迹换个坐标,就能给移动机器人用吗?
人工智能·算法·机器学习
David猪大卫2 小时前
【C++修炼】map 和 set 的使用
c语言·开发语言·数据结构·c++·经验分享·笔记·学习
HPtiger2 小时前
C++ 入门笔记:从 C 到 C++ 的核心过渡(命名空间、输入输出、缺省参数、重载、引用、内联)
c语言·c++·学习
星恒随风2 小时前
MySQL 从零入门(三):表的约束详解
linux·服务器·数据库·笔记·mysql
vvilkim4 小时前
数据结构类型、区别与应用场景:一篇讲透选型逻辑
数据结构
安妮细水长流6 小时前
ad7124
c语言·单片机
蒸蒸yyyyzwd6 小时前
从后端面经学到的 3 个知识
笔记·学习
17岁的勇气6 小时前
Unity Shader unity文档学习笔记(二十二):shader变体
笔记·学习·unity
坤坤子吖7 小时前
Linux网络编程:自定义应用层协议与 JSON 序列化
linux·服务器·网络·c++·笔记·json
lisw058 小时前
Dayhoff的PAM替换矩阵理论评介!
线性代数·算法·矩阵