最近学习数据结构,其中二叉树是一个重难点,于是着手用C++把二叉搜索树的基本属性和操作实现了一遍。在coding的过程中,感觉自己对C++的一些语法细节和编程规范也有了更深的理解,下面贴出源码,代码中给出了必要的注释,方便自己整理思路,同时也给各位初学者以参考。
我使用的参考书是 《数据结构与算法实现——C语言描述》 原书第二版 Mark Allen Weiss 著 冯舜玺 译
Node.h
#ifndef _NODE_H_
#define _NODE_H_
class Node {
friend class Tree;
friend class avlTree;
public:
Node() {}
Node(const int num_) : num(num_), lchild(nullptr), rchild(nullptr), parent(nullptr) {}
Node(const Node* n) : num(n->num), lchild(nullptr), rchild(nullptr), parent(nullptr) {}
Node& operator=(const Node& n) {
num = n.num;
lchild = rchild = parent = nullptr;
return *this;
}
int getNum() {
return num;
}
private:
Node* lchild;
Node* rchild;
Node* parent;
int num;
};
#endif // #ifndef _NODE_H_
Tree.h
#include "Node.h"
#include <ostream>
class Tree {
//以友元函数的形式重载"<<"运算符
friend std::ostream& operator<<(std::ostream& os, const Tree& t);
public:
Tree() : root(nullptr) {}//指针一定要初始化,避免因野指针而导致运行时出错
Tree(const Tree& t);
Tree(int a[], int length);
Tree::~Tree() {
emptySubTree(root);
}
//重载"="运算符
Tree& operator=(const Tree& t) {
//return Tree(t);//不能返回临时对象的引用!
this->copyTree(t);
return *this;
}
const Node* getRoot() const;
void copyTree(const Tree& t);
Node* insertNode(int num);
bool deleteNode(int num);
void emptySubTree(Node* NodePtr);
void emptyTree();
Node* minNode();
Node* maxNode();
void preorderTraversal() const;
void preorderTraversal(Node* NodePtr) const;
void inorderTraversal() const;
void inorderTraversal(Node* NodePtr) const;
void postorderTraversal() const;
void postorderTraversal(Node* NodePtr) const;
Node* findNode(const int num);
Node* findNode(const int num, Node* NodePtr) const;
void showTree(const Node *show_node) const;
void showSubtree(const Node *show_node) const;
void showTree() const;
private:
void copyChild(Node* Nodeptr, const Node* p);
Node* insertNode(int num, Node* &NodePtr);
bool deleteNode(int num, Node* NodePtr);
bool deleteNode(int num, Node* NodePtr, int);
Node* minNode(Node* Nodeptr) const;
Node* maxNode(Node* Nodeptr) const;
void showSubtree(const Node *show_node, int& layer) const;
void showNode(const Node *show_node, int& layer) const;
Node* root;
};
SearchTree.cpp
#include <cassert>
#include <iostream>
#include <iomanip>
#include <stack>
#include "Tree.h"
using namespace std;
//通过数组构造一棵二叉搜索树
Tree::Tree(int a[], int length) {
root = nullptr;//构造之前需将root指针置空,否则在insertNode()中无法判断是否为空树!
for (int i = 0; i < length; ++i)
insertNode(a[i], root);
}
//拷贝构造函数
Tree::Tree(const Tree& t) {
root = nullptr;//拷贝构造之前需将root指针置空,否则在emptyTree()中会出错!
copyTree(t);
}
//获取根节点
const Node* Tree::getRoot() const {
return root;
}
//************************************************************************//
//查找子树的最小节点
Node* Tree::minNode(Node* Nodeptr) const {
if (Nodeptr == nullptr) {
cout << "Tree/SubTree is empty!" << endl;
return nullptr;
} else {
while (Nodeptr->lchild != nullptr) {
Nodeptr = Nodeptr->lchild;
}
return Nodeptr;
}
}
//查找整棵树的最小节点
Node* Tree::minNode() {
return minNode(root);
}
//************************************************************************//
//查找子树的最大节点
Node* Tree::maxNode(Node* Nodeptr) const {
if (Nodeptr == nullptr) {
cout << "Tree/SubTree is empty!" << endl;
return nullptr;
} else {
while (Nodeptr->rchild != nullptr) {
Nodeptr = Nodeptr->rchild;
}
return Nodeptr;
}
}
//查找整棵树的最大节点
Node* Tree::maxNode() {
return maxNode(root);
}
//************************************************************************//
//在子树中查找指定的值
Node* Tree::findNode(const int num, Node* NodePtr) const {
if (NodePtr == nullptr) {
cout << "Error: An empty tree/subtree!" << endl;
return nullptr;
}
while (NodePtr->num != num) {
if (num > NodePtr->num) {
if (NodePtr->rchild != nullptr)
NodePtr = NodePtr->rchild;
else {
cout << "ERROR: " << num << " is not found!" << endl;
return nullptr;
}
} else {
if (NodePtr->lchild != nullptr)
NodePtr = NodePtr->lchild;
else {
cout << "Error: " << num << " is not found!" << endl;
return nullptr;
}
}
}
assert(NodePtr->num == num);//必定已找到
return NodePtr;
}
//在整棵二叉搜索树中查找指定的值
Node* Tree::findNode(const int num) {
return findNode(num, root);
}
//************************************************************************//
//往二叉搜索树插入一个节点
Node* Tree::insertNode(int num, Node* &NodePtr) {
if (NodePtr == nullptr)//空树
NodePtr = new Node(num);
else if (num > NodePtr->num) {
if (NodePtr->rchild != nullptr)
NodePtr->rchild = insertNode(num, NodePtr->rchild);
else {
NodePtr->rchild = new Node(num);
NodePtr->rchild->parent = NodePtr;
}
}
else if (num < NodePtr->num) {
if (NodePtr->lchild != nullptr)
NodePtr->lchild = insertNode(num, NodePtr->lchild);
else {
NodePtr->lchild = new Node(num);
NodePtr->lchild->parent = NodePtr;
}
}
//else {该点已经存在于树中,不做任何操作}
return NodePtr;//返回插入操作之后指向新树根节点的指针
}
Node* Tree::insertNode(int num) {
return insertNode(num, root);
}
//************************************************************************//
//删除节点。递归版本,利用哑元实现重载
bool Tree::deleteNode(int num, Node* NodePtr, int) {
if (NodePtr == nullptr) {
cout << "Error: Attempt to delete a nonexistent number node!" << endl;
return false;
}
if (num > NodePtr->num)
deleteNode(num, NodePtr->rchild);
else if (num < NodePtr->num)
deleteNode(num, NodePtr->lchild);
else {//已经找到需要删除的节点
if (NodePtr->lchild && NodePtr->rchild) {//有左右子节点
NodePtr->num = minNode(NodePtr->rchild)->num;//用右子树的最小节点值替换删除点的值
deleteNode(NodePtr->num, NodePtr->rchild);//删除右子树的最小节点
} else {
if (NodePtr->rchild != nullptr) {//只有左子节点
NodePtr->parent->lchild = NodePtr->rchild;
NodePtr->rchild->parent = NodePtr->parent;
}
else if (NodePtr->lchild != nullptr) {//只有右子节点
NodePtr->parent->lchild = NodePtr->lchild;
NodePtr->lchild->parent = NodePtr->parent;
}
else {//没有子节点,判断其自身是左叶子节点还是右叶子节点
if (NodePtr->parent->lchild == NodePtr)
NodePtr->parent->lchild = nullptr;//指针置空,避免出现野指针
else
NodePtr->parent->rchild = nullptr;
}
delete NodePtr;
}
return true;
}
}
//删除节点。另一递归版本
bool Tree::deleteNode(int num, Node* NodePtr) {
NodePtr = findNode(num, NodePtr);//非递归方式查找
if (NodePtr == nullptr) {//未找到指定节点
cout << "Error: Attempt to delete a nonexistent number node!" << endl;
return false;
}
if (NodePtr->lchild && NodePtr->rchild) {//同时有左右子节点,则采用替换删除
NodePtr->num = minNode(NodePtr->rchild)->num;
return deleteNode(NodePtr->num, NodePtr->rchild);
} else {//子节点数不超过1个,可直接删除
if (NodePtr->rchild != nullptr) {
NodePtr->parent->lchild = NodePtr->rchild;
NodePtr->rchild->parent = NodePtr->parent;
} else if (NodePtr->lchild != nullptr) {
NodePtr->parent->lchild = NodePtr->lchild;
NodePtr->lchild->parent = NodePtr->parent;
} else {
if (NodePtr->parent->lchild == NodePtr)
NodePtr->parent->lchild = nullptr;
else
NodePtr->parent->rchild = nullptr;
}
delete NodePtr;
return true;
}
}
bool Tree::deleteNode(int num) {
//递归版本
//return deleteNode(num, root, 0);
//非递归版本
return deleteNode(num, root);
}
//************************************************************************//
//清空子树
void Tree::emptySubTree(Node* NodePtr) {
static int count = 0;
if (NodePtr != nullptr) {
++count; //cout << "递归:" << count << endl;
emptySubTree(NodePtr->lchild);
emptySubTree(NodePtr->rchild);
--count; //cout << "返回:" << count << endl;
if (NodePtr != root) {//非根节点
if (0 == count) {//最后一次删除,需前将指向该节点的指针置空(其所有孩子节点内的指针成员已经delete掉)
if (NodePtr->parent->lchild == NodePtr)
NodePtr->parent->lchild = nullptr;
else
NodePtr->parent->rchild = nullptr;
//cout << "置空:" << NodePtr->num << endl;
}
delete NodePtr;
} else {//根节点
delete root;
root = nullptr;//删除后必须置空,否则会成为空悬指针!
//NodePtr = nullptrptr;//此句无效,NodePtr是root的一个指针副本!
}
}
}
//清空整棵树
void Tree::emptyTree() {
emptySubTree(root);
}
//************************************************************************//
//前序遍历子树
void Tree::preorderTraversal(Node* NodePtr) const
{
if (NodePtr != nullptr) {
cout << NodePtr->num << " ";
preorderTraversal(NodePtr->lchild);
preorderTraversal(NodePtr->rchild);
}
}
void Tree::preorderTraversal() const {
preorderTraversal(root);
}
//中序遍历子树
void Tree::inorderTraversal(Node* NodePtr) const
{
if (NodePtr != nullptr) {
inorderTraversal(NodePtr->lchild);
cout << NodePtr->num << " ";
inorderTraversal(NodePtr->rchild);
}
}
void Tree::inorderTraversal() const {
inorderTraversal(root);
}
//后序遍历子树
void Tree::postorderTraversal(Node* NodePtr) const
{
if (NodePtr != nullptr) {
postorderTraversal(NodePtr->lchild);
postorderTraversal(NodePtr->rchild);
cout << NodePtr->num << " ";
}
}
void Tree::postorderTraversal() const {
postorderTraversal(root);
}
//************************************************************************//
//将二叉树以文件目录树的形式显示,不存在的子节点用'*'代替
//版本一:用堆栈保存每个节点的状态,递归打印整棵树
void Tree::showTree(const Node *show_node) const{
static int num = -1;
static stack<bool> flag;
if (show_node != nullptr) {//打印非空节点,且检查并保存其子节点的状态
++num;
for (int i = 0; i < num; ++i)
cout << " ";
cout.flags(ios::left);
cout << setw(4) << show_node->num << endl;
if (show_node->rchild == nullptr && show_node->lchild == nullptr) {
flag.push(false);//左右子节点指针都为空即叶子节点,保存两个状态(需要连续pop两个状态)
flag.push(false);
} else if(show_node->lchild == nullptr || show_node->rchild == nullptr )
flag.push(true);//有且仅有一个子节点指针为空,保存其状态,下一次需要打印一个替代符
showTree(show_node->lchild);//左节点先打印
showTree(show_node->rchild);//右节点后打印
--num;
} else {//打印空节点替代符
if (num != -1) {//非空树。如果是空树,在递归执行时第一次就进入此分支,有num == -1
if (flag.top()) {
for (int i = 0; i <= num; i++)
cout << " ";
cout << setw(4) << '*' << endl;
}
flag.pop();//弹出节点状态
}
}
}
//版本二:用“前序遍历”思想直接递归打印指定的子树
void Tree::showSubtree(const Node *show_node, int& layer) const {
if (!layer) {//根节点
if (show_node != nullptr)//非空树则打印根节点
showNode(show_node, layer);
else
return;
}
if (show_node != nullptr) {//非空节点
if (show_node->lchild || show_node->rchild) {//非叶子节点,则递归打印子节点
++layer;
showNode(show_node->lchild, layer);
showSubtree(show_node->lchild, layer);
showNode(show_node->rchild, layer);
showSubtree(show_node->rchild, layer);
--layer;
}
}
}
//包装showSubtree()方法
void Tree::showSubtree(const Node *show_node) const {
int layer = 0;
showSubtree(show_node, layer);//必须给layer传0,显示根节点时不打印空格
}
//打印显示单个节点值或替代符,layer代表节点的深度
void Tree::showNode(const Node *show_node, int& layer) const {
for (int i = 0; i < layer; ++i)
cout << " ";
cout.flags(ios::left);
if (show_node != nullptr)
cout << setw(4) << show_node->num << endl;
else
cout << setw(4) << '*' << endl;
}
//打印显示整棵二叉树
void Tree::showTree() const {
//版本一
//showTree(root);
//版本二
showSubtree(root);
}
//************************************************************************//
//拷贝一棵树
void Tree::copyTree(const Tree& t) {
emptyTree();//清空自身
if (t.root != nullptr) {
root = new Node(t.root);//拷贝根节点
copyChild(root, t.root);//递归拷贝每个子节点
}
}
//拷贝当前节点的子节点
void Tree::copyChild(Node* Nodeptr, const Node* p) {
if (p->lchild != nullptr) {
Nodeptr->lchild = new Node(p->lchild);
Nodeptr->lchild->parent = Nodeptr;
copyChild(Nodeptr->lchild, p->lchild);
}
if (p->rchild != nullptr) {
Nodeptr->rchild = new Node(p->rchild);
Nodeptr->rchild->parent = Nodeptr;
copyChild(Nodeptr->rchild, p->rchild);
}
}
//************************************************************************//
//重载"<<"运算符
ostream& operator<<(ostream& os, const Tree& t) {
t.inorderTraversal();//默认输出中序遍历结果,即升序显示
return os;//保证"<<"运算符可以连续输出
}
TestTree.cpp
#include <iostream>
#include "Tree.h"
using namespace std;
int main() {
int a[10] = { 3, 5, -5, 11, 2, 4, 5, 63, -15, 0};//有重复值5
Tree tree1(a, sizeof(a)/sizeof(int));
Tree tree2(tree1);
//Tree tree3 = tree2;//初始化对象时编译器会优化,直接调用了拷贝构造函数
Tree tree3;
tree3 = tree2;
cout << "tree1: " << tree1 << endl << "tree2: " << tree2 << endl << "tree3: " << tree3 << endl;
tree1.insertNode(1);
cout << "tree1.insertNode(1): " << tree1 << endl;
tree1.insertNode(-5);//插入已经存在的点
cout << "tree1.insertNode(5): " << tree1 << endl;
tree1.deleteNode(3);//删除根节点
cout << "tree1.deleteNode(3): " << tree1 << endl;
tree1.deleteNode(8);//删除不存在的节点
cout << "tree1.deleteNode(8): " << tree1 <<endl;
cout << "tree1目录树:" << endl;
tree1.showTree();
return 0;
}
测试输出结果为: