二叉树(binary tree)是有限个元素的集合(允许为空)。当二叉树非空时,其中有一个称为根的元素,余下的元素(如果有的话)被组成 2个二叉树,分别称为左子树和右子树。
和树的主要区别是:
二叉树可以为空,树不能为空。
二叉树中每个元素都恰好有两棵子树(其中一个或两个可能为空)。而树中每个节点有若干子树且没有左右这样的区分。
包含n个节点的二叉树的高度最大为n,最小为log(n+1),树的高度和节点个数无关。
二叉树常用的描述方式是链表形式,每个元素都用一个节点结构体来表示,节点包括两个指针LeftChild和RightChild分别指向左子树和右子树,除此两个指针外,每个节点还有一个data表示当前元素的数值。
节点的默认构造函数构造一个空节点,不仅不存在data值且左右子树都为空;带有一个int型参数的构造函数,构造当前节点,但是左右子树为空;第三种情况是参数带有一个int值和两个子树指针。
//1.默认构造函数
BinaryTreeNode() { _LeftChild = _RightChild = 0; }
//2.带有一个数值的构造函数
BinaryTreeNode(const T& value) {
_data = value;
_LeftChild = _RightChild = 0;
}
//3.带有数值和左右子树指针的构造
BinaryTreeNode(const T& value, BinaryTreeNode *l, BinaryTreeNode *r) {
_data = value;
_LeftChild = l;
_RightChild = r;
}
二叉树的主要操作包括构造树、计算高度、计算节点数、复制、遍历、删除整棵树、插入节点等等。
1.二叉树的构造
创建一个空节点作为根节点,即创建一个空树。
2.计算高度
最常见的方式是利用递归分别计算左右子树的高度,取大者加一就是当前树的高度了。
//树的高度
size_type Height() const {
return Height(_root);
}
重载了一个私有的成员函数 ,递归计算节点的左右子树的高度,并比较两者的大小。
//节点的高度
size_type Height(BinaryTreeNode<value_type> *node) const {
if (!node)
return 0;
size_type lh = Height(node->_LeftChild); //递归计算左子树高度
size_type rh = Height(node->_RightChild); //递归计算右子树高度
return (lh > rh) ? (lh + 1) : (rh + 1);
}
3.遍历
树的三种遍历,都采用递归的思想实现,以前序遍历为例:
//对二叉树进行前序遍历
void PreOrder(void(*Visit) (BinaryTreeNode<value_type> *u), BinaryTreeNode<value_type> *node) {
if (node) {
Visit(node); //对根节点进行处理
PreOrder(Visit, node->_LeftChild); //前序遍历左子树
PreOrder(Visit, node->_RightChild); //前序遍历右子树
}
}
引入了一个函数指针参数,供其他需要遍历的功能使用,例如统计节点数、删除树和打印节点等的时候,调用不同的处理根节点的方法就可以了。
除此以外还有一种层序遍历,和其他三种有点差别。
4.统计节点数
肯定要遍历整个树,这里三种遍历方式结果一样,以前序遍历为例:
//节点个数
size_type size() {
_count = 0;
PreOrder(Add, _root);
return _count;
}
其中Add是一个私有的静态成员函数,访问到某个节点时,将_count参数加1。
5.删除整棵树
需要先删除子节点再删除父节点,所以这里只能采用后序遍历逐个节点销毁的方式:
//删除一棵树
void destroy() {
PostOrder(DeleteNode, _root); //后序遍历并逐个删除节点
_root = nullptr;
}
其中DeleteNode是一个私有的静态成员函数,访问到某个节点时,将其内存回收。
6.插入节点
需要注意的是需要判断节点是否为空,还要做一次判断将新节点插入左右子节点位置:
//插入节点
void insert(BinaryTreeNode<value_type> *node, const value_type &value) {
if (!node) {
node = new BinaryTreeNode(value);
return;
}
if (value < node->_data) {
insert(node->_LeftChild, value);
} else {
insert(node->_RightChild, value);
}
}
7.根据值搜索节点
bool find(const value_type value) const {
return find(value, _root);
}
重载一个私有的静态成员函数,递归的在左右子树中搜索当前的数值:
bool find(const value_type value, BinaryTreeNode<value_type> *node) const {
if (!node)
return false;
if (node->_data == value)
return true;
return find(value, node->_LeftChild) || find(value, node->_RightChild);
}
需要注意判断是否递归到叶子节点。
二叉树功能测试代码
int main()
{
BinaryTree<int> t, t1, t2, t3;
t2.MakeTree(1, t, t);
t3.MakeTree(2, t, t);
t1.MakeTree(3, t2, t3);
t2.MakeTree(4, t1, t);
cout << "树的节点数:" << t2.size() << endl;
cout << "前序遍历: " << endl;
t2.DisplayPreOrder();
cout << "中序遍历: " << endl;
t2.DisplayInOrder();
cout << "后序遍历: " << endl;
t2.DisplayPostOrder();
cout << "树的高度: " << t2.Height() << endl;
cout << endl;
if (t2.find(3)) {
cout << "找到节点" << endl;
} else {
cout << "没找到节点" << endl;
}
cout << endl;
t2.destroy(); //删除树
if (t2.empty()) {
cout << "树已被删除" << endl;
}
cout << "树的高度: " << t2.Height() << endl;
system("pause");
return 0;
}
测试结果