手记

二叉搜索树BST在C++类中的实现(增删改查)【Van0512】

网上能够查到的数据结构相关资料,多是用C语言代码实现。
C语言是面向过程的,C++是面向对象的,尽管它们有诸多的相同之处,尽管把C语言实现BST的代码拷贝在C++中差不多就能pass了。但既然学习了C++,就尽可能使用面向对象的思想,将函数封装在类中,而不是在main函数外面写了一个又一个的函数。那样的话,即使是用了一些C++的语法,整个程序也可以说是标准的C语言式。

BST,Binary Search Tree,是一种很典型并且广泛使用的数据结构。它的特点是左边节点的数据值比该节点小,右边节点的数据值比该节点大。利用这一点,在AVL Tree(这是BST的升级版,以后再写)中可以实现O(log n)的查找效率。

在这边,BSTree类提供了插入节点、删除节点、搜索节点、判断空树,以及四中遍历方式等功能(修改?搜索出来就可以改了)
其中建树、添加节点、遍历等,需要注意的点都不多,唯独删除节点。

  1. 因为删除的节点只能是目标节点一个,而对其下的子树(若有),所有的节点必须保留。
  2. 而且删除目标节点之后,还需要保证该二叉树还是BST。
    至于删除目标节点,以及其下所有子孙节点的函数,实现起来比较简单。有需要的慕友可自行增加这一函数。

代码设计:

  1. 节点类。一些函数在节点类中比较容易实现,把这个任务交给它。还有一些辅助函数,删除节点函数所需要的,也在节点类中设计。
  2. 树类。提供建一颗空树、判空、根据数据增加节点、查找、删除的功能,以及前序、中序、后序、层序,四种遍历。
  3. 测试

BSTree类的头文件BSTree.h以及实现文件BSTree.cpp如下:

#pragma once
#include "Node.h"

class BSTree {
public:
    BSTree();       //建一颗空树。
    bool isEmpty(); //判断树是否为空。
    void insertNode(ElemType &elem);    //传入元素值,插入一个节点
    bool deleteNode(ElemType &elem);    //删除给定元素值的节点。
    Node* searchNode(ElemType &elem);   //查找给定元素值的节点,返回其地址。

    void preOrderTaversal();        //前序、中序、后序以及层序遍历。
    void inOrderTaversal();
    void postOrderTaversal();
    void levelOrderTaversal();
private:
    Node *rootPtr;
};
#include "BSTree.h"
#include <iostream>

BSTree::BSTree() {
    this->rootPtr = NULL;
}

bool BSTree::isEmpty() {
    return NULL == rootPtr;
}

void BSTree::insertNode(ElemType &elem) {
    if (isEmpty())
        rootPtr = new Node(elem);
    //若树非空,插入一个新节点的操作在Node类中更容易实现,所以把这个任务交给Node类。
    else
        rootPtr->addNode(elem);
}

bool BSTree::deleteNode(ElemType &elem) {
    Node *objNode = searchNode(elem);
    if (NULL == objNode) return false;
    //树根节点的情况比较特殊,单独考虑。
    if (objNode == rootPtr && !(rootPtr->leftChild != NULL && rootPtr->rightChild != NULL)) {
        if (rootPtr->leftChild == NULL && rootPtr->rightChild == NULL) 
            rootPtr = NULL;
        else if (rootPtr->leftChild == NULL) {      //rootPtr->rightChild != NULL
            rootPtr = rootPtr->rightChild;
            rootPtr->parent = NULL;
        }
        else if (rootPtr->rightChild == NULL) {
            rootPtr = rootPtr->leftChild;
            rootPtr->parent = NULL;
        }
        delete objNode;
        //树根有两个子节点的情况可做一般性处理。
    }
    else
        objNode->eraseNode();

    return true;
}

Node* BSTree::searchNode(ElemType &elem) {
    if (isEmpty()) return NULL;
    //若树非空,搜索指定元素值的节点在Node类中也更容易实现,同样将这个任务交给Node类。
    return rootPtr->findNode(elem);
}

void BSTree::preOrderTaversal() {
    if (!isEmpty()) rootPtr->traversalByPreOrder();
}

void BSTree::inOrderTaversal() {
    if (!isEmpty()) rootPtr->traversalByInOrder();
}

void BSTree::postOrderTaversal() {
    if (!isEmpty()) rootPtr->traversalByPostOrder();
}

void BSTree::levelOrderTaversal() {
    if (!isEmpty()) rootPtr->traversalByLevelOrder();
}

Node类的头文件和实现文件如下:

#pragma once

typedef int ElemType;
class Node {
public:
    Node(ElemType data = 0);
    void addNode(ElemType &elem);   
    Node* findNode(ElemType &elem); //返回给定元素值的节点的地址,若找不到,返回NULL。
    Node* findMin();        //返回以此节点为根节点的树的元素值最小的节点。
    Node* findMax();
    /*
    删除当前节点,若有子节点,保留,并保证依然是BST结构。
    在BSTree类中,特别处理了树根节点的情况。
    */
    void eraseNode();   

    void traversalByPreOrder();     //前序、中序、后序以及层序遍历以此节点为根节点的树。
    void traversalByInOrder();
    void traversalByPostOrder();
    void traversalByLevelOrder();
public:
    ElemType data;
    Node *parent;
    Node *leftChild;
    Node *rightChild;
};
#include "Node.h"
#include <iostream>
#include <queue>
using namespace std;

Node::Node(ElemType data /* = 0 */) {
    this->data = data;
    parent = leftChild = rightChild = NULL;
}

void Node::addNode(ElemType &elem) {
    if (elem < data) {
        if (leftChild == NULL) {
            leftChild = new Node(elem);
            leftChild->parent = this;
        }
        else
            leftChild->addNode(elem);
    }
    else if (elem > data) {
        if (rightChild == NULL) {
            rightChild = new Node(elem);
            rightChild->parent = this;
        }
        else
            rightChild->addNode(elem);
    }
    //do nothing when 'elem == data'.
}

Node* Node::findNode(ElemType &elem) {
    if (elem == data) return this;
    else if (elem < data && leftChild != NULL)
        return leftChild->findNode(elem);
    else if (elem > data && rightChild != NULL)
        return rightChild->findNode(elem);
    return NULL;
}

Node* Node::findMin() {
    Node *tmp = this;
    while (tmp->leftChild != NULL)
        tmp = tmp->leftChild;
    return tmp;
}

Node* Node::findMax() {
    Node *tmp = this;
    while (tmp->rightChild != NULL)
        tmp = tmp->rightChild;
    return tmp;
}

/*
1. 两个孩子节点都存在的情况:在右子树找最小值,或在左子树找最大值,
填充根节点,再删掉找出来的节点(这个节点顶多只有一个子节点,变成简单的情况)。
2. 有一个孩子节点,或者没有孩子节点的情况。
*/
void Node::eraseNode() {
    if (leftChild != NULL && rightChild != NULL) {
        Node *tmp = leftChild->findMax();
        data = tmp->data;
        tmp->eraseNode();
    }
    else {
        if (leftChild == NULL) {    //左孩子节点是NULL,有右孩子节点,或者没有子节点。
            if (this == parent->leftChild) 
                parent->leftChild = rightChild;
            else 
                parent->rightChild = rightChild;
            if (rightChild != NULL) rightChild->parent = parent;
        }
        else {  //有左孩子,右孩子为NULL
            if (this == parent->leftChild)
                parent->leftChild = leftChild;
            else
                parent->rightChild = leftChild;
            leftChild->parent = parent;
        }
    }
}

void Node::traversalByPreOrder() {
    cout << data << " ";
    if (leftChild != NULL)
        leftChild->traversalByPreOrder();
    if (rightChild != NULL)
        rightChild->traversalByPreOrder();
}

void Node::traversalByInOrder() {
    if (leftChild != NULL)
        leftChild->traversalByInOrder();
    cout << data << " ";
    if (rightChild != NULL)
        rightChild->traversalByInOrder();
}

void Node::traversalByPostOrder() {
    if (leftChild != NULL)
        leftChild->traversalByPostOrder();
    if (rightChild != NULL)
        rightChild->traversalByPostOrder();
    cout << data << " ";
}

void Node::traversalByLevelOrder() {
    queue<Node *> que;
    que.push(this);
    while (!que.empty()) {
        Node *tmp = que.front();
        que.pop();
        cout << tmp->data << " ";
        if (tmp->leftChild != NULL)
            que.push(tmp->leftChild);
        if (tmp->rightChild != NULL)
            que.push(tmp->rightChild);
    }
}

测试文件如下:

#include "BSTree.h"
#include <iostream>
using namespace std;

int main(void) {
    BSTree *bst = new BSTree;
    cout << boolalpha << "空树?" << bst->isEmpty() << endl;
    int intArr[] = { 6, 5, 8, 4, 7, 9, 2, 1, 3 };
    for (int i = 0; i < sizeof(intArr) / sizeof(int); i++) {
        bst->insertNode(intArr[i]);
    }
    /*
    建起来的这棵树长这模样:
                                    6   
                                /       \
                              5          8
                            /           /   \
                          4           7      9
                        /
                      2
                    /   \
                  1      3
    */
    cout << boolalpha << "空树?" << bst->isEmpty() << endl;

    //测试 deleteNode() 函数
    int elem = 10;
    bst->deleteNode(elem);
    elem = 3;
    bst->deleteNode(elem);
    elem = 4;
    bst->deleteNode(elem);
    elem = 8;
    bst->deleteNode(elem);
    elem = 6;
    bst->deleteNode(elem);

    bst->preOrderTaversal();
    cout << "先序遍历" << endl;
    bst->inOrderTaversal();
    cout << "中序遍历" << endl;
    bst->postOrderTaversal();
    cout << "后序遍历" << endl;
    bst->levelOrderTaversal();
    cout << "层序遍历" << endl;

    delete bst;
    system("pause");
    return 0;
}

慕友也可将之修改成一个类模板,但要切记,许多编译器不支持类模板的头文件和实现文件分离,要把它们都放在头文件中。

14人推荐
随时随地看视频
慕课网APP

热门评论

排版都不知道怎么搞,纠结o(╯□╰)o。

要我用一块又一块的代码框框起来吗?

查看全部评论