写了两天的二叉搜索树的操作原理,今天就把操作的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环境下编译通过