数据结构——树(9)——二叉搜索树的操作实现C++代码

写了两天的二叉搜索树的操作原理,今天就把操作的C++代码拿出来大家分享。

头文件BST.h

#include <ostream>
#include <istream>

using namespace std;

/*定义节点结构体的数据结构*/
struct Node{
    string str;
    Node *left;
    Node *right;

    /*新节点的构造函数*/
    Node(string s){
        str = s;
        left = NULL;
        right = NULL;
    }
};

/*因为我们是操作,所以我们把这些封装在一个类里面*/
class stringSet{
public:
    stringSet(); //构造函数
    ~stringSet();//析构函数

    void add(string s); //向BST中添加元素s
    string findMin(); //返回字母表中最小的单词(如果找不到,那么我们就返回空字符)
    string findMax();//返回字母表中最大的单词(如果找不到,那么我们就返回空字符)
    bool isContains(string s); //判断BTS中是否含有s元素
    void remove(string s);//从BST中移除某个s元素
    bool isEmpty(); //判断BST是否为空
    int size();//返回二叉树的大小
    friend ostream &operator << (ostream &out, stringSet & set);

#include "BSTpriv.h"
};

私有头文件BSTpriv.h

private:
    Node *root; //我们需要指向树根的指针
    int count; //因为我们要判断树的大小,因此我们要设置一个计数变量
    //下面定义一些辅助函数,目的是为了减少主接口间方法的参数复杂度,隐藏更多底层信息
    void add(string s, Node *&node);
    void add(string s, Node **node);
    string findMin(Node *node);
    string findMax(Node *node);
    bool isContains(string s, Node *node);
    Node *remove(string s, Node *node, Node *parent);
    void inOrderTraversal(ostream &out, Node *node, string &max);
    void postOrderClear(Node *node);

实现文件BSTimple.cpp

这部分文件中,移除操作很难,尽量理解吧,不过说实话很棒的代码。逻辑性极强!

#include <string>
#include "BST.h"

//构造函数
stringSet::stringSet(){
    root = NULL; //目前树是空的
    count = 0; //因此树中没有元素
}

//析构函数
stringSet::~stringSet(){
    postOrderClear(root);//后序遍历,然后边遍历边删除遍历到的节点
    root = NULL; //清除后就没有树了
}

//后序遍历删除法(LRD)
void stringSet::postOrderClear(Node *node) {
    // 先执行后序遍历,然后边遍历边删除,采用递归
    if (node == NULL) {
        return;
    }
    postOrderClear(node->left);
    postOrderClear(node->right);
    delete node;
}

//将数据添加到BST中
void stringSet::add(string s){
    add(s,root);//使用第一个辅助函数,那么参数的形式为引用参数
    //add(s,&root); //使用第二个辅助函数,那么参数形式为root的地址
}

//第一个辅助函数
void stringSet::add(string s, Node *&node){
    if(node == NULL){                   //基础事件(base case)
        node = new Node(s);
        count++;
    }else if(node -> str > s){        //用else if是因为这样的情况只会出现某一种,提高效率
        add(s, node -> left);         //因为参数是引用,因此我们用的参数是变量的名称
    }else if (node -> str < s){
        add(s,node -> right);
    }
}
//第二个辅助函数,利用指向指针的指针
void stringSet::add(string s, Node **node){
    if (*node == NULL){  //基础事件,如果树根为空,
        *node  = new Node (s); //新建一个含s的节点,并赋值给指向树根的指针
        count++;
    }else if ((*node) -> str > s){  //当插入值s小于节点指向的数值,插入左子树
        add(s, &((*node )-> left));//因为参数是指向指针的指针,因此我们用的参数是变量的地址
    }else if ((*node) -> str < s){
        add(s, &((*node) ->right));
    }
}

//寻找二叉搜索树中的最小值,使用辅助函数
string stringSet::findMin(){
    return findMin(root);
}
//findMin辅助函数的具体实现,重载该函数,利用递归的思想,
string stringSet::findMin(Node *node){
    //基础事件
    if(node == NULL){
        return ""; //情况一:如果节点为空或者找不到这样的节点,返回空字符
    }
    if(node -> left == NULL){ //情况二:如果该节点的左孩子为空,即不再有左孩子,那么它就是最小值,返回对应的数值
        return (node -> str);
    }
    return findMin(node -> left); //否则,对该节点的左孩子进行递归寻最小值操作
}

//寻找二叉搜索树中的最大值,如果树为空,则直接返回空
string stringSet::findMax(){
    return findMax(root);
}
//寻找二叉搜索树中的最大值的辅助函数,利用递归实现
string stringSet::findMax(Node *node){
    if(node == NULL){
        return ""; //基础事件,情况一:如果节点为空,那么返回空字符
    }
    if(node -> right == NULL){ // 情况二:当当前节点的右孩子为空的时候,返回当前节点的数值,因为没有更右的值
        return (node -> str);
    }
    return findMax(node -> right);
}
//判断二叉搜索树中是否含有某个节点值,实际就是寻找二叉搜索树中的某个节点
bool stringSet::isContains(string s){
    if (root == NULL){
        return false;
    }
    return isContains(s, root);
}

//二叉搜索树辅助函数isContains的具体实现
bool stringSet::isContains(string s, Node*node){
    if(node == NULL){
        return false;
    }
    if(node -> str == s){
        return true;
    }
    //搜索左右子树
    return isContains(s,node -> left) || isContains(s, node -> right);
}

//从二叉搜索树中移除某个数值,比较难的实现
void stringSet::remove(string s){
    if(root != NULL){
        Node *removeNode; //新建一个node类型的指针
        if(root -> str == s){ //如果树根恰好就是我们要移除的值,我们前面说过特殊处理
            //创建一个虚拟的节点,并将树根值设为这个节点的左孩子
            Node dummyNode("");
            //注意 这里不能用dummyNode -> left = root,因为dummyNode不是指针类型
            dummyNode.left = root; 
            removeNode = remove(s, root, &dummyNode);
            root = dummyNode.left;
        }else{
            removeNode = remove(s, root, NULL);
        }
        if(removeNode != NULL){
            delete removeNode;
            count--;
        }
    }
}
 //重载remove函数
Node * stringSet::remove(string s, Node *node, Node *parent){
    //遍历相应的子树,直到找到我们要删除的节点
    if(s < node -> str){ //此时要删除的节点应该在左子树
        if(node -> left != NULL){ //如果存在左孩子
            return remove(s, node -> left, node); //递归寻找对应的s,并删除
        }else{
            return NULL; //当我们要删除的数值在树中不存在
        }
    }else if(s > node -> str){ //此时此时要删除的节点应该在右子树,解释同上
        if(node -> right != NULL){
            return remove(s, node ->right, node);
        }else{
            return NULL;
        }
    }else{ //我们找到了要删除的节点node
        if(node ->left != NULL && node ->right != NULL){ //情况一,有两个孩子
            node -> str = findMin(node -> right);//将此节点的值用右子树的最小值代替
            return remove(node ->str, node ->right,node);//递归删除右子树的最小值
        }else if(parent -> left == node){
            //将父节点的左侧替换为该节点的右侧或左侧子节点
            //取决于哪一个存在(如果它没有子元素,则右边是NULL)
            parent -> left = (node -> left != NULL) ? node ->left : node ->right;
            return node;
        }else if(parent ->right == node){
            parent -> right = (node->left != NULL) ? node->left : node->right;
            return node;
        }
    }
    return NULL; //这一行永远不会到达,但是没有这一行编译时会有问题
}

//返回树的大小
int stringSet::size(){
    return count;
}

//判断树是否为空
bool stringSet::isEmpty(){
    return root == NULL;
}

//重载运算符<< 使得树中的值能按中序遍历输出
ostream &operator <<(ostream &out, stringSet &set){
    string max = set.findMax();
    out << "[";
    set.inOrderTraversal(out,set.root,max);
    out << "]";
    return out;
}

//按顺序遍历树,并将每个节点的值添加到流中
//注意:需要“最大”,所以我们不会在最后一个节点的值后面输出一个逗号
//(因为我们希望它好看!)
void stringSet::inOrderTraversal(ostream &out, Node *node, string &max){
    if(node == NULL){
        return ;
    }
    inOrderTraversal(out, node -> left, max);  //中序遍历,LDR
    out << node -> str;
    if (node->str != max) {
        out << ", ";
    }
    inOrderTraversal(out, node->right, max);
}

测试文件BSTtest.cpp

#include <iostream>
#include <string>
#include "BST.h"

using namespace std;

int main() {
    cout << "创建一组字符集" << endl;
    stringSet ss;

    cout << "是否为空? " << (ss.isEmpty() ? "true" : "false") << endl;
    cout << "计数? " << ss.size() << endl << endl;

    cout << "正在添加\"cat\"" << endl; // \为转义字符
    ss.add("cat");
    cout << "是否为空? " << (ss.isEmpty() ? "true" : "false") << endl;
    cout << "计数? " << ss.size() << endl << endl;

    cout << "正在添加 \"dog\"" << endl;
    ss.add("dog");
    cout << "计数? " << ss.size() << endl << endl;

    cout << "接着添加 fish, zebra, aardvark, lion, marketeer, marmot, bird, eagle" << endl;
    ss.add("fish");
    ss.add("zebra");
    ss.add("aardvark");
    ss.add("lion");
    ss.add("marketeer");
    ss.add("marmot");
    ss.add("bird");
    ss.add("eagle");

    cout << "计数? " << ss.size() << endl << endl;

    cout << "全集为 : " << ss << endl << endl;

    cout << "是否包含单词 \"fish\"? " << (ss.isContains("fish") ? "true" : "false") << endl;
    cout << "是否包含单词 \"hamster\"? " << (ss.isContains("hamster") ? "true" : "false") << endl;

    cout << "单词表中第一个为? " << (ss.findMin()) << endl;
    cout << "单词表中最后一个为? " << ss.findMax() << endl;

    cout << "移除单词 \"bird\"" << endl;
    ss.remove("bird");
    cout << "此时全集为 : " << ss << endl << endl;
    cout << "计数? " << ss.size() << endl << endl;

    cout << "移除单词 \"lion\"" << endl;
    ss.remove("lion");
    cout << "此时全集为 : " << ss << endl << endl;
    cout << "计数? " << ss.size() << endl << endl;

    cout << "移除单词 \"fish\"" << endl;
    ss.remove("fish");
    cout << "全集为 : " << ss << endl << endl;
    cout << "计数? " << ss.size() << endl << endl;

    cout << "移除单词 \"cat\" (树根)" << endl;
    ss.remove("cat");
    cout << "全集为 set: " << ss << endl << endl;
    cout << "计数为? " << ss.size() << endl << endl;
    return 0;
}

测试结果

在VS2010环境下编译通过
这里写图片描述

猜你喜欢

转载自blog.csdn.net/redrnt/article/details/79239156