二叉查找树的特点
对于树中的每个节点 n,它的左子树中所有节点的值均小于 n节点的值,而它的右子树中所有节点的值均大于 n节点的值。
//二叉树的节点
struct Node
{
Type element;
Node *left;
Node *right
};
关于重复值的问题有三种情况:
- 将重复的数据忽略,只保留一个。
- 将重复的数据放在不同节点,但是这样会破坏树节点“左小右大”的特点。
- 在每个节点都加一个计数器,记录重复值有多少个。
第三种情况稍微复杂一点,对应的节点数据结构如下:
struct Node
{
Type element;
int count;
Node *left;
Node *right;
};
//count就是添加的计数器。
/*相应的,对数据的添加、删除等操作也要对计数器的值进行判断*/
二叉查找树类
/*
这里实现了一些二叉查找树的基本功能:
1. 插入数据(左小右大)
2. 检索数据集中是否包含某个数据
3. 删除单个数据节点
4. 将树置空(数据全部删除)
5. 打印数据集
*/
#include <iostream>
template <typename T>
class BinarySearchTree
{
public:
BinarySearchTree();
BinarySearchTree(const BinarySearchTree & bst);
~BinarySearchTree();
const T & findMin() const;
const T & findMax() const;
bool contains(const T & x) const;
bool isEmpty() const;
void printTree() const;
void makeEmpty();
void insert(const T & x);
void insert(T && x);
void remove(const T & x);
private:
struct Node
{
T element;
Node *left;
Node *right;
/*节点的拷贝*/
Node(const T & theElement, Node *lt, Node *rt) : element{ theElement }, left{ lt }, right{ rt } { }
Node(T && theElement, Node *lt, Node *rt) : element{ std::move(theElement) }, left{ lt }, right{ rt } { }
};
Node *root; //根节点
void insert(const T & x, Node * & t); // * & 是指针的引用传递
void insert(T && x, Node * & t); // && 是右值引用
void remove(const T & x, Node * & t);
Node * findMin(Node *t) const;
Node * findMax(Node *t) const;
bool contains(const T & x, Node * t) const;
void makeEmpty(Node * & t);
void printTree(Node *t) const;
Node * clone(Node *t) const; //树的拷贝
};
成员函数实现
构造函和析构函数
/*无参构造函数*/
template <typename T>
BinarySearchTree<T>::BinarySearchTree() : root{ nullptr }
{
}
/*
拷贝构造函数
调用了clone方法,从树根开始递归的进行节点拷贝
*/
template <typename T>
BinarySearchTree<T>::BinarySearchTree(const BinarySearchTree & bst) : root{ nullptr }
{
root = clone(bst.root); //从根开始递归拷贝
}
/*clone方法*/
template <typename T>
typename BinarySearchTree<T>::Node* BinarySearchTree<T>::clone(Node *t) const
{
if (t == nullptr)
return nullptr;
else
return new Node{ t->element, clone(t->left), clone(t->right) };
}
/*析构函数:调用了makeEmpty方法*/
template <typename T>
BinarySearchTree<T>::~BinarySearchTree()
{
makeEmpty();
}
/*makeEmpty*/
template <typename T>
void BinarySearchTree<T>::makeEmpty()
{
makeEmpty(root);
}
/*递归删除*/
template <typename T>
void BinarySearchTree<T>::makeEmpty(Node * & t)
{
if (t != nullptr)
{
makeEmpty(t->left);
makeEmpty(t->right);
delete t;
}
t = nullptr;
}
数据插入和删除
/*插入数据,这里忽略了重复的数据*/
template <typename T>
void BinarySearchTree<T>::insert(const T & x)
{
insert(x, root);
}
template <typename T>
void BinarySearchTree<T>::insert(T && x)
{
insert(x, root);
}
template <typename T>
void BinarySearchTree<T>::insert(const T & x, Node * & t)
{
if (t == nullptr)
t = new Node(x, nullptr, nullptr);
else if (x < t->element)
insert(x, t->left);
else if (x > t->element)
insert(x, t->right);
else
; //重复元则不做插入
}
template <typename T>
void BinarySearchTree<T>::insert(T && x, Node * & t)
{
if (t == nullptr)
t = new Node(std::move(x), nullptr, nullptr);
else if (x < t->element)
insert(std::move(x), t->left);
else if (x > t->element)
inset(std::move(x), t->right);
else
; //重复元不做插入
}
/*删除数据(比较复杂)*/
template <typename T>
void BinarySearchTree<T>::remove(const T & x)
{
remove(x, root);
}
/*
树叶:和有一个子节点的情况一起。因为树叶有两个空链,调整父节点的链即可
有一个子节点:调整父节点的链
有两个子节点:用右子树的最小数据代替该节点的数据,并删除右子树最小的节点(也可以用左子树的最大节点,效果一样)
*/
template <typename T>
void BinarySearchTree<T>::remove(const T & x, Node * & t)
{
if (t == nullptr) //空树直接返回
return;
if (x < t->element)
remove(x, t->left); //递归查找目标节点
else if (x > t->element)
remove(x, t->right); //递归查找目标节点
else if ((t->left != nullptr) && (t->right != nullptr)) //有两个儿子
{
t->element = findMin(t->right)->element; //右子树最小数据替换该节点数据
remove(t->element, t->right); //删除右子树最小节点
}
else //有一个儿子 或者 没有儿子
{
Node *oldNode = t;
t = (t->left != nullptr) ? t->left : t->right;
delete oldNode;
}
}
查找数据
/**
* 是否包含某个数据
*/
template <typename T>
bool BinarySearchTree<T>::contains(const T & x) const
{
return contains(x, root);
}
template <typename T>
bool BinarySearchTree<T>::contains(const T & x, Node * t) const
{
if (t == nullptr)
return false;
else if (x < t->element)
return contains(x, t->left);
else if (x > t->element)
return contains(x, t->right);
else
return true;
}
/**
* 查找最大数据
*/
template <typename T>
const T & BinarySearchTree<T>::findMax() const
{
typename BinarySearchTree<T>::Node *temp;
temp = findMax(root);
int max = temp->element;
return max;
}
template <typename T>
typename BinarySearchTree<T>::Node * BinarySearchTree<T>::findMax(Node *t) const
{
if (t == nullptr)
return nullptr;
if (t->right == nullptr)
return t;
return findMax(t->right);
}
/**
* 查找最小值
*/
template <typename T>
const T & BinarySearchTree<T>::findMin() const
{
typename BinarySearchTree<T>::Node *temp;
temp = findMin(root);
int min = temp->element;
return min;
}
template <typename T>
typename BinarySearchTree<T>::Node * BinarySearchTree<T>::findMin(Node *t) const
{
if (t == nullptr)
return nullptr;
if (t->left == nullptr)
return t;
return findMin(t->left);
}
判断是否空树和打印
/**
* 判断是否为空树
*/
template <typename T>
bool BinarySearchTree<T>::isEmpty() const
{
if (root == nullptr)
return true;
else
return false;
}
/**
* 打印树--中序遍历
*/
template <typename T>
void BinarySearchTree<T>::printTree() const
{
if (isEmpty())
cout << "空树“ << endl;
else
printTree(root);
}
template <typename T>
void BinarySearchTree<T>::printTree(Node *t) const
{
if (t == nullptr)
return;
printTree(t->left);
std::cout << t->element << " ";
printTree(t->right);
}
用个小例子测试二叉树
#include "BinarySearchTree.h"
#include <iostream>
#include <ctime>
using namespace std;
#define random(x) (rand()%x)
int main()
{
srand((int)time(0));
BinarySearchTree<int> bt;
//插入数据:1-20
for (int i = 20; i >= 1; --i)
{
bt.insert(random(101));
}
//打印数据
bt.printTree();
cout << endl;
//测试拷贝构造函数
BinarySearchTree<int> bt2(bt);
//打印bt2
cout << "bt2: ";
bt2.printTree();
cout << endl;
//查找最大数据
cout << bt.findMax() << endl;
//查找最小值
cout << bt.findMin() << endl;
//查看是否为空树
if (bt.isEmpty())
cout << "空树" << endl;
else
cout << "不是空树" << endl;
//查看是否包含10
if (bt.contains(10))
cout << "10在数据集里" << endl;
else
cout << "10不在数据集里" << endl;
//删除10
bt.remove(10);
//查看是否包含10
if (bt.contains(10))
cout << "10在数据集里" << endl;
else
cout << "10不在数据集里" << endl;
//置空
bt.makeEmpty();
//查看是否为空树
if (bt.isEmpty())
cout << "空树" << endl;
else
cout << "不是空树" << endl;
return 0;
}