数据结构——二叉搜索树

在计算机科学中,数据结构是构建高效算法的基石。二叉搜索树(Binary Search Tree,简称BST)作为一种基本的数据结构,不仅在理论学习中占有重要地位,也在实际应用中广泛使用。本文将深入探讨BST的原理、特点和应用,并提供一个C++实现的示例,帮助读者更好地理解和运用这一结构。

什么是二叉搜索树?

二叉搜索树是一种具有以下特性的二叉树:

  • 每个节点都包含一个键(key)和最多两个子节点。
  • 左子节点的键值总是小于其父节点的键值。
  • 右子节点的键值总是大于或等于其父节点的键值。
  • 每个子树也都是二叉搜索树。

这些特性使得BST在数据存储、搜索、插入和删除操作上非常高效。

C++中的BST实现

我们以一个简单的模板类来实现BST。这个实现包含了节点的定义、树的基本操作(如插入、搜索、删除)和辅助功能(如打印树结构)。

节点类定义

我们首先定义一个内部的Node类,用于存储树中的数据和子节点的指针

#include
using namespace std;

template
class BST {
    // ... [类定义,包括私有Node类和方法定义]
public:
    BST() : root(nullptr) {}
    ~BST() { deleteTree(root); }
    void insert(T data) { root = insert(root, data); }
    bool search(T data) { return search(root, data); }
    void remove(T data) { root = remove(root, data); }
    void print() { printTree(root); cout << endl; }
};

使用BST类

要使用这个BST类,您只需创建一个BST对象,并使用其方法来操作树

BST myTree;
myTree.insert(5);
myTree.insert(3);
myTree.insert(7);
myTree.remove(3);
if (myTree.search(7)) {
    cout << "找到了数字7!" << endl;
}
myTree.print();

树的操作方法

BST类主要包括插入、搜索和删除节点的方法。这些方法利用BST的特性,通过递归操作来管理树的结构。

在private中实现以下内容,在public中进行调用

插入操作

插入操作首先判断当前节点是否为空,然后根据值的大小递归地插入左子树或右子树。

Node* insert(Node* node, T data) {
    if (node == nullptr) {
        return new Node(data);
    }
    if (data < node->data) {
        node->left = insert(node->left, data);
    } else if (data > node->data) {
        node->right = insert(node->right, data);
    }
    return node;
}
搜索操作

搜索操作检查树中是否存在特定值的节点,利用BST的性质来提高搜索效率。

bool search(Node* node, T data) {
    if (node == nullptr) {
        return false;
    }
    if (data < node->data) {
        return search(node->left, data);
    } else if (data > node->data) {
        return search(node->right, data);
    }
    return true;
}
删除操作

删除操作是最复杂的,需要处理三种情况:

  1. 被删除节点没有子节点
  2. 有一个子节点
  3. 有左右两个节点
 //删除节点
 Node* remove(Node* root, T data) {
     //找到要删除的节点
     if (root == nullptr) return root;
     //如果要删除的节点小于根节点,递归左子树
     if (data < root->data) {
         root->left = remove(root->left, data);
     }//如果要删除的节点大于根节点,递归右子树
     else if (data > root->data) {
         root->right = remove(root->right, data);
     }//如果要删除的节点等于根节点
     else {
         //如果要删除的节点没有左子树
         if (root->left == nullptr) {
             Node* temp = root->right;
             delete root;
             return temp;
         }//如果要删除的节点没有右子树
         else if (root->right == nullptr) {
             Node* temp = root->left;
             delete root;
             return temp;
         }
         //如果要删除的节点有左右子树
         //找到右子树中最小的节点
         //将该节点的值赋值给要删除的节点
         //删除该节点
         
         Node* temp = findMinNode(root->right);
         root->data = temp->data;
         root->right = remove(root->right, temp->data);
     }
     //返回根节点
     return root;
 }
  打印树结构

我们还实现了一个辅助方法来打印树的内容,这有助于验证树的结构和操作是否正确。

void printTree(Node* root) {
    if (root != nullptr) {
        printTree(root->left);
        cout << root->data << "\t";
        printTree(root->right);
    }
}
在public中调用
public:
    // Constructor
    BST() : root(nullptr) {}
    // Destructor
    ~BST() {
        deleteTree(root);
    }
    //在外部调用时,调用私有成员函数
    void insert(T data) {
        //如果根节点为空,直接插入
        root = insert(root, data);
    }
    //在外部调用时,调用私有成员函数
    bool search(T data) {
        //如果根节点为空,返回false
        return search(root, data);
    }
    //在外部调用时,调用私有成员函数
    void remove(T data) {
        //如果根节点为空,直接返回
        root = remove(root, data);
    }
    //在外部调用时,调用私有成员函数
    void print() {
        printTree(root);
        std::cout << endl;
    }

代码实现

#include

using namespace std;

// Purpose: Implementation of BST class
template
class BST {
private:
    // Purpose: Node class for BST
    class Node {
    public:
        T data;
        Node* left;
        Node* right;
        // Constructor
        Node(T newdata) : data(newdata), left(nullptr), right(nullptr) {}
    };

    Node* root;
    //插入节点
    Node* insert(Node* node, T data) {
        //如果节点为空,返回新节点
        if (node == nullptr) {
            return new Node(data);
        }//如果节点不为空,判断要插入的值与节点的值的大小
        if (data < node->data) {
            node->left = insert(node->left, data);
        }//如果要插入的值小于节点的值,递归左子树
        else if (data > node->data) {
            node->right = insert(node->right, data);
        }//如果要插入的值大于节点的值,递归右子树
        return node;
    }
    //查找节点
    bool search(Node* node, T data) {
        //如果节点为空,返回false
        if (node == nullptr) {
            return false;
        }//如果节点不为空,判断要查找的值与节点的值的大小
		else if (data < node->data) {
            return search(node->left, data);
        }//如果要查找的值大于节点的值,递归右子树
        else if (data > node->data) {
            return search(node->right, data);
        }
        //如果要查找的值等于节点的值,返回true
        return true;
    }
    //找到右子树中最小的节点
    Node* findMinNode(Node* node) {
        //如果节点为空,返回空
        Node* current = node;
        if (current == nullptr) return current;

        //如果节点不为空,一直向左找,直到找到最小的节点
        while (current && current->left != nullptr) {
            current = current->left;
        }
        return current;
    }
    //删除节点
    Node* remove(Node* root, T data) {
        //找到要删除的节点
        if (root == nullptr) return root;
        //如果要删除的节点小于根节点,递归左子树
        if (data < root->data) {
            root->left = remove(root->left, data);
        }//如果要删除的节点大于根节点,递归右子树
        else if (data > root->data) {
            root->right = remove(root->right, data);
        }//如果要删除的节点等于根节点
        else {
            //如果要删除的节点没有左子树
            if (root->left == nullptr) {
                Node* temp = root->right;
                delete root;
                return temp;
            }//如果要删除的节点没有右子树
            else if (root->right == nullptr) {
                Node* temp = root->left;
                delete root;
                return temp;
            }
            //如果要删除的节点有左右子树
            //找到右子树中最小的节点
            //将该节点的值赋值给要删除的节点
            //删除该节点
            
            Node* temp = findMinNode(root->right);
            root->data = temp->data;
            root->right = remove(root->right, temp->data);
        }
        //返回根节点
        return root;
    }
    //删除树
    void deleteTree(Node* node) {
        if (node) {
            //递归删除左右子树
            deleteTree(node->left);
            deleteTree(node->right);
            delete node;
        }
    }
    //打印树
    void printTree(Node*root) {
        if (!root)return;
        cout << root->data << "\t";
        printTree(root->left);
        printTree(root->right);
    }
public:
    // Constructor
    BST() : root(nullptr) {}
    // Destructor
    ~BST() {
        deleteTree(root);
    }
    //在外部调用时,调用私有成员函数
    void insert(T data) {
        //如果根节点为空,直接插入
        root = insert(root, data);
    }
    //在外部调用时,调用私有成员函数
    bool search(T data) {
        //如果根节点为空,返回false
        return search(root, data);
    }
    //在外部调用时,调用私有成员函数
    void remove(T data) {
        //如果根节点为空,直接返回
        root = remove(root, data);
    }
    //在外部调用时,调用私有成员函数
    void print() {
        printTree(root);
        std::cout << endl;
    }
};

结论

通过这个简单的BST实现,我们可以看到BST如何在保持结构的同时提供高效的数据操作。虽然这个实现是基础的(并没有迭代器的设计),但它为理解更复杂的数据结构和算法提供了良好的起点。随着对BST的深入研究,您可以探索更高级的概念,如树的平衡和旋转,以及它们在实际应用中的表现。

你可能感兴趣的:(数据结构与算法,数据结构)