树
树(Tree)是n(n>=0)个结点的有限集合。n = 0时称为空树。在任意一颗非空树中:(1)有且仅有一个特定的称为根(Root)的结点;(2)当n > 1时,其余结点可分为m (m > 0)个互不相交的有限集 T1 T 1 、 T2 T 2 、 …… 、 Tm T m ,其中每一个集合本身又是一个树,并且称为根的子树(SubTree),如下图所示:
树的结点包含一个数据元素及若干指向其子树的分支。结点拥有的子树数称为结点的度(Degree)。度为0的结点称为叶结点(Leaf)或终端结点;度不为0的结点称为非终端结点或分支结点。除根结点之外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。
结点的子树的根称为该结点的孩子(Child),相应地,该结点称为还的双亲(Parent)。同一个双亲的孩子之间互称兄弟(Sibling)。结点的祖先是从根到结点所经分支上的所有结点。对于 F F 来说 B B 、 A A 都是它的祖先。以某结点为根的子树中的任一结点都称为该结点的子孙。 B B 的子树有 E E 、 F F 、 G G 。如下图:
树的存储结构
树的存储结构有多种,常用的有双亲表示法、孩子表示法、孩子兄弟表示法。
双亲表示法
结构如下:
data | parent |
---|
其中data是数据域,存储结点的数据信息。而parent是指针域,指向该结点的双亲。
template < typename T>
struct TreeNode
{
T data;
TreeNode * parent;
}
之前的树的存储结构如下:
孩子表示法
data | child1 | child2 | child3 | … | childd |
---|
template <typename T>
struct TreeNode
{
T data;
TreeNode * nodeList;
}
孩子兄弟表示法
data | firstchild | rightsib |
---|
template <typename T>
struct TreeNode
{
T data;
TreeNode * firstchild, * rightsib;
}
二叉树
二叉树(Binary Tree)是 n ( n >= 0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两颗互不相交的、分别称为根结点的左子树和右子树的二叉树组成。如下图:
二叉树结点结构一般如下:
struct TreeNode
{
T data;
TreeNode * lchild, * rchild;
}
二叉树遍历
二叉树遍历常用的有4中,分别为前序遍历、中序遍历、后序遍历。其中的前、中、后是按照根结点的遍历顺序来区分的。
前序遍历
若二叉树为空,则返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。上图二叉树的前序遍历顺序为:ABFHKECFIGJ。前序遍历的代码如下:
void preOrderTraverse(TreeNode * node)
{
if (nullptr == node)
return;
std::cout << node->data << std::endl;
preOrderTraverse(node->lchild);
preOrderTraverse(node->rchild);
}
中序遍历
若二叉树为空,则返回,否则先中序遍历根结点的左子树,然后访问根结点,最后中序遍历右子树。上图二叉树的中序遍历顺序为:HKDBEAIFCGJ。中序遍历的代码如下:
void inOrderTraverse(TreeNode * node)
{
if (nullptr == node)
return;
inOrderTraverse(node->lchild);
std::cout << node->data << std::endl;
inOrderTraverse(node->rchild);
}
后序遍历
若二叉树为空,则返回,否则先后序遍历根结点的左子树,然后后序遍历右子树,最后访问根结点。上图二叉树的后序遍历顺序为:KHDEBIFJGCA。后序遍历的代码如下:
void postOrderTraverse(TreeNode * node)
{
if (nullptr == node)
return;
postOrderTraverse(node->lchild);
postOrderTraverse(node->rchild);
std::cout << node->data << std::endl;
}
二叉查找树
二叉查找树(Binary Sort Tree),又称为二叉排序树。它或者是一个树,或者是就有下列性质的二叉树。
若它的左子树不空,则左子树上所有结点的值均小于它的跟结构的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉排序树。
构造一颗二叉查找树的目的,不是为了排序,目的是为了提高查找和出啊如删除关键字的速度。
二叉查找树查找
二叉树的查找没什么难度,利用要查找的值,经过比较和递归之类的不难实现:
template<typename T>
void searchElement(TreeNode<T> * node, T key)
{
if (nullptr == node)
return nullptr;
if (key == node->data)
return node;
else if (key < node->data)
searchElement(node->lchild, key);
else
searchElement(node->rchild, key);
}
二叉查找树插入
插入操作和查找差不多,如下:
template<typename T>
bool insertElement(TreeNode<T> *& node,T key)
{
if (nullptr == node)// 空树
{
node = new TreeNode<T>;
node->data = key;
}
else if (key == node->data)// 已经有该关键字
{
return false;
}
else
{
if (key < node->data)
{
insertElement(node->lchild, key);
}
else
{
insertElement(node->rchild, key);
}
}
}
二叉查找树删除
删除稍微麻烦一点,要分好几种情况:
1、被删除的结点没有孩子。这种情况最简单。直接删除就可以了。
2、被删除的结点只有左孩子或者只有右孩子。这种情况需要删除当前结点,并把当前结点的孩子作为当前结点的父结点的孩子。说白了就是用当前结点的孩子替代当前结点,然后把当前结点删除了。
3、被删除结点同时有左孩子和右孩子。这种情况稍微复杂一点,因为二叉查找树的特性在替换当前结点的时候做些操作。
我们把二叉查找树按照中序遍历后输出结果中的序列中,每个元素的前一个元素称为该元素的前驱,后面一个元素称为该元素的后继。如下图:
上图是一个二叉查找树,按照中序遍历的结果是3、10、20、32、33、44、45、50。则33的前驱为32,后继为44。由二叉查找树的特性可知如果一个二叉搜索树中的一个结点有两个孩子,那么它的后继没有左孩子,它的前驱没有右孩子。
假如想删除结点33,则符合情况3。而情况3则是用当前结点的后继代替当前结点,当前结点的左孩子为后继结点的左孩子,后继结点如果有右孩子,则把右孩子当作后继结点父结点的左孩子。如下图:
实现代码如下:
template<typename T>
void replaceAByB(TreeNode<T> *A, TreeNode<T> *B)
{
if (A == nullptr)
{
return;
}
TreeNode<T> * parent = getParent(A->data);
if (nullptr == parent)
{
m_root = B;
return;
}
if (parent->lchild == A)
{
parent->lchild = B;
}
else
{
parent->rchild = B;
}
}
template<typename T>
TreeNode<T> * minElement(TreeNode<T> * node)
{
if (nullptr == node->lchild)
return node;
else
minElement(node->lchild);
}
template<typename T>
bool deleteElement(T key)
{
TreeNode<T> * pCurrent = search(key);
if (nullptr == pCurrent)
return false;
if (nullptr == pCurrent->lchild)// 无左孩子
{
replaceAByB(pCurrent, pCurrent->rchild);
}
else if (nullptr == pCurrent->rchild)// 无右孩子
{
replaceAByB(pCurrent, pCurrent->lchild);
}
else
{
TreeNode<T> * pSuccesor = minElement(pCurrent->rchild);// 找到后继结点
replaceAByB(pSuccesor, pSuccesor->rchild);// 后继者是没有左孩子的
pSuccesor->rchild = pCurrent->rchild;
pSuccesor->lchild = pCurrent->lchild;
replaceAByB(pCurrent, pSuccesor);
}
delete pCurrent;
}
搜索二叉树的完整实现代码如下,为了方便这里把树封装为一个类,二叉树继承自树,二叉查找树继承自二叉树,后面要说的平衡二叉查找树继承自二叉查找树。
#pragma once
#include <iostream>
// 树节点
template<typename T>
class TreeNode
{
public:
T data;
TreeNode<T>* lchild,* rchild;
TreeNode():lchild(nullptr), rchild(nullptr) {}
};
//------------------------------------------------------------------
// 树
template<typename T>
class Tree
{
public:
virtual bool insert(T key) = 0;
virtual bool deleteElement(T key) = 0;
virtual TreeNode<T>* search(T key) = 0;
virtual TreeNode<T> * getParent(T key) = 0;
virtual void deleteAll() = 0;
Tree(): m_root(nullptr) {}
protected:
TreeNode<T> * m_root;
};
//-----------------------------------------------------------------
// 二叉树
template<typename T>
class BiTree : public Tree<T>
{
public:
BiTree(){}
virtual bool insert(T key);
virtual bool deleteElement(T key);
virtual TreeNode<T> * search(T key);
virtual TreeNode<T> * getParent(T key);
virtual void deleteAll();
virtual void preOrderTraverse();
virtual void inOrderTraverse();
virtual void postOrderTraverse();
protected:
virtual void preTraverse(TreeNode<T> * node);
virtual void inTraverse(TreeNode<T> * node);
virtual void postTraverse(TreeNode<T> * node);
virtual void deleteAllElement(TreeNode<T> *& node);
};
template<typename T>
void BiTree<T>::preOrderTraverse()
{
preTraverse(m_root);
}
template<typename T>
void BiTree<T>::inOrderTraverse()
{
inTraverse(m_root);
}
template<typename T>
void BiTree<T>::postOrderTraverse()
{
postTraverse(m_root);
}
template<typename T>
void BiTree<T>::preTraverse(TreeNode<T> * node)
{
if (node)
{
std::cout << node->data << std::endl;
preTraverse(node->lchild);
preTraverse(node->rchild);
}
}
template<typename T>
void BiTree<T>::inTraverse(TreeNode<T> * node)
{
if (node)
{
inTraverse(node->lchild);
std::cout << node->data << std::endl;
inTraverse(node->rchild);
}
}
template<typename T>
void BiTree<T>::postTraverse(TreeNode<T> * node)
{
if (node)
{
postTraverse(node->lchild);
postTraverse(node->rchild);
std::cout << node->data << std::endl;
}
}
template<typename T>
TreeNode<T> * BiTree<T>::getParent(T key)
{
return nullptr;
}
template<typename T>
bool BiTree<T>::insert(T key)
{
return true;
}
template<typename T>
bool BiTree<T>::deleteElement(T key)
{
return true;
}
template<typename T>
TreeNode<T>* BiTree<T>::search(T key)
{
return nullptr;
}
template<typename T>
void BiTree<T>::deleteAll()
{
deleteAllElement(m_root);
}
template<typename T>
void BiTree<T>::deleteAllElement(TreeNode<T> *& node)
{
if (nullptr == node)
return;
if (nullptr != node->lchild)
deleteAllElement(node->lchild);
else if (nullptr != node->rchild)
deleteAllElement(node->rchild);
node->lchild = nullptr;
node->rchild = nullptr;
delete node;
node = nullptr;
}
//-------------------------------------------------------------------
// 二叉查找树(Binary Sort Tree)BST
template<typename T>
class TreeBST : public BiTree<T>
{
public:
virtual bool insert(T key);
virtual bool deleteElement(T key);
virtual TreeNode<T>* search(T key);
virtual TreeNode<T> * getParent(T key);
TreeNode<T> * minElement(TreeNode<T> * node);
TreeNode<T> * maxElement(TreeNode<T> * node);
protected:
virtual bool insertElement(TreeNode<T> * & node, T key);
virtual TreeNode<T> * searchElement(TreeNode<T> * node, T key);
TreeNode<T> * getPar(TreeNode<T> * node, T key);
virtual void replaceAByB(TreeNode<T> * A, TreeNode<T> * B);
};
template<typename T>
bool TreeBST<T>::insert(T key)
{
return insertElement(m_root, key);
}
template<typename T>
bool TreeBST<T>::insertElement(TreeNode<T> *& node,T key)
{
if (nullptr == node)// 空树
{
node = new TreeNode<T>;
node->data = key;
}
else if (key == node->data)// 已经有该关键字
{
return false;
}
else
{
if (key < node->data)
{
insertElement(node->lchild, key);
}
else
{
insertElement(node->rchild, key);
}
}
}
template<typename T>
bool TreeBST<T>::deleteElement(T key)
{
TreeNode<T> * pCurrent = search(key);
if (nullptr == pCurrent)
return false;
if (nullptr == pCurrent->lchild)// 无左孩子
{
replaceAByB(pCurrent, pCurrent->rchild);
}
else if (nullptr == pCurrent->rchild)// 无右孩子
{
replaceAByB(pCurrent, pCurrent->lchild);
}
else
{
TreeNode<T> * pSuccesor = minElement(pCurrent->rchild);// 找到后继结点
replaceAByB(pSuccesor, pSuccesor->rchild);// 后继者是没有左孩子的
pSuccesor->rchild = pCurrent->rchild;
pSuccesor->lchild = pCurrent->lchild;
replaceAByB(pCurrent, pSuccesor);
}
delete pCurrent;
}
template<typename T>
TreeNode<T>* TreeBST<T>::search(T key)
{
return searchElement(m_root, key);
}
template<typename T>
TreeNode<T> * TreeBST<T>::searchElement(TreeNode<T> * node, T key)
{
if (nullptr == node)
return nullptr;
if (key == node->data)
return node;
else if (key < node->data)
searchElement(node->lchild, key);
else
searchElement(node->rchild, key);
}
template<typename T>
TreeNode<T> * TreeBST<T>::getParent(T key)
{
return getPar(m_root, key);
}
template<typename T>
TreeNode<T> * TreeBST<T>::getPar(TreeNode<T> * node, T key)
{
static TreeNode<T> * parentNode = nullptr;
if (nullptr == node)
return nullptr;
if (key == node->data)
{
// 这里是因为返回一个值后,static指的是上一次返回结果,
// 所以返回后需要重新赋值,用static是为了在递归过程中减少传递的参数
TreeNode<T> * temp = parentNode;
parentNode = nullptr;
return temp;
}
else if (key < node->data)
{
parentNode = node;
getPar(node->lchild, key);
}
else
{
parentNode = node;
getPar(node->rchild, key);
}
}
template<typename T>
TreeNode<T> * TreeBST<T>::minElement(TreeNode<T> * node)
{
if (nullptr == node->lchild)
return node;
else
minElement(node->lchild);
}
template<typename T>
TreeNode<T> * TreeBST<T>::maxElement(TreeNode<T> * node)
{
if (nullptr == node->rchild)
return node;
else
maxElement(node->rchild);
}
template<typename T>
void TreeBST<T>::replaceAByB(TreeNode<T> *A, TreeNode<T> *B)
{
if (A == nullptr)
{
return;
}
TreeNode<T> * parent = getParent(A->data);
if (nullptr == parent)
{
m_root = B;
return;
}
if (parent->lchild == A)
{
parent->lchild = B;
}
else
{
parent->rchild = B;
}
}
测试代码如下:
#include "tree.h"
#include <iostream>
int main()
{
int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
TreeBST<int> tree;
for (int i = 0; i < 15; i++)
{
tree.insert(ptr[i]);
}
tree.inOrderTraverse();
std::cout << "删除元素" << std::endl;
tree.deleteElement(2);// 情况1
tree.deleteElement(5);// 情况2
tree.deleteElement(4);// 情况3
tree.deleteElement(9);// 情况3
tree.inOrderTraverse();
tree.deleteAll();
getchar();
return 0;
}
测试结果如下图:
平衡二叉查找树
平衡二叉树(Self-Balancing Binary Search Tree 或 Height-Balanced Binary Search Tree),以一种二叉搜索树,其中每一个结点的左子树和右子树的高度差至多等于1。我们将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF(Balance Factor)。
平衡二叉查找树插入
平衡二叉树构建的基本思想就是在构建二叉搜索树的过程中,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡子树。在爆出二叉搜索树特性的前提下,调整最小不平衡子树中个结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。
如构建序列为:3、2、1、4、5、6、7、10、9、8的平衡二叉树,构建过程如下:
如上图,在步骤3中,结点3的平衡因子为2大于1,所以需要做出一些调整。如步骤4,此时结点成为了根结点,结点3成为了结点2的右孩子。我们称这种操作为以结点3为原点的右旋(顺时针)。继续插入操作,接下来是结点4。
在步骤6中,结点3的平衡因子的绝对值大于1,所以要做一些调整,如步骤7,以结点3为原点,向左旋转。这就是以结点3为原点的左旋(逆时针)。继续:
在步骤8中,结点2的平衡因子超标,需要调整,如步骤9,以结点2为原点,进行左旋。继续:
在步骤10中,结点5需要调整,如步骤11,以结点5为原点进行左旋。继续:
在步骤13中,结点7需要调整,但是结点7的右孩子(即结点10)的平衡因子与自身平衡因子符号不同。所以先以结点7的右孩子由原点进行右旋(如步骤14),然后再以当前结点,也就是结点7为原点进行左旋(如步骤15)。继续:
再步骤16中结点6,需要调整,同样因为结点6的右孩子平衡因子与自己不同,所以先以结点6右孩子为原点进行右旋(如步骤17),然后以结点6为原点进左旋(如步骤18)。
由以上步骤我们知道,如果当前结点平衡因子大于1,并且左孩子的平衡因子符号与当前结点相同。则需要以当前结点为原点右旋。如果左孩子平衡因子符号与当前结点符号不同,则先以左孩子为原点进行左旋,然后以当前结点为原点进行右旋。同理,如果当前结点平衡因子小于-1,并且右孩子平衡因子符号与当前结点相同,则以当前结点为原点左旋。如果右孩子平衡因子符号与当前结点不同,则先以右孩子为原点进行右旋,然后以当前结点为原点进行左旋。
左旋实现:
template<typename T>
void leftRotate(TreeNode<T> * node)
{
TreeNode<T> temp;// 临时对象,用来中转根节点
TreeNode<T> * rchild = node->rchild;
temp = * node;
// 构建根节点
* node = * node->rchild;
// 构建根节点的左孩子节点
temp.rchild = rchild->lchild;
* rchild = temp;
node->lchild = rchild;
}
右旋实现:
template<typename T>
void rightRotate(TreeNode<T> * node)
{
TreeNode<T> temp;// 临时对象,用来中转根节点
TreeNode<T> * lchild = node->lchild;
temp = * node;
// 构建根节点
* node = * node->lchild;
// 构建根节点的有孩子节点
temp.lchild = lchild->rchild;
* lchild = temp;
node->rchild = lchild;
}
为方便操作和代码重用,把树结构调整封装入两个函数,分别为左平衡操作和右平衡操作。如果左子树高度大于右子树高度超过1则左平衡操作,反之右平衡操作。这里的平衡因子只取了三个值,分别为eh(等高)、lh(左高)、rh(右高)。实现代码如下:
// 平衡二叉树结点(AVL)
template<typename T>
class TreeNodeAVL : public TreeNode<T>
{
public:
enum {eh, lh, rh} m_bf;// 分别为等高,左高,右高
TreeNodeAVL() : m_bf(eh) {}
};
template<typename T>
void TreeAVL<T>::leftBalance(TreeNodeAVL<T> * node)
{
// 调用此函数时,node->m_bf == RH,即右边高于左边
TreeNodeAVL<T> * lchildNode = (TreeNodeAVL<T> * )node->lchild, * tempNode = nullptr;
switch (lchildNode->m_bf)
{
case TreeNodeAVL<T>::eh:// 平衡因子
node->m_bf = TreeNodeAVL<T>::lh;
lchildNode->m_bf = TreeNodeAVL<T>::rh;
rightRotate(node);
break;
case TreeNodeAVL<T>::lh:// 和左孩子平衡因子相同
node->m_bf = TreeNodeAVL<T>::eh;
lchildNode->m_bf = TreeNodeAVL<T>::eh;
rightRotate(node);
break;
case TreeNodeAVL<T>::rh:// 和左孩子的平衡因子不同,则先以左孩子为中心左旋旋,然后再右旋
tempNode = (TreeNodeAVL<T> * )lchildNode->rchild;// 右旋会影响到左孩子的右孩子的平衡因子
switch (tempNode->m_bf)
{
case TreeNodeAVL<T>::eh:
node->m_bf = TreeNodeAVL<T>::eh;
lchildNode->m_bf = TreeNodeAVL<T>::eh;
break;
case TreeNodeAVL<T>::lh:
node->m_bf = TreeNodeAVL<T>::eh;
lchildNode->m_bf = TreeNodeAVL<T>::rh;
break;
case TreeNodeAVL<T>::rh:
node->m_bf = TreeNodeAVL<T>::lh;
lchildNode->m_bf = TreeNodeAVL<T>::eh;
break;
default:
break;
}
tempNode->m_bf = TreeNodeAVL<T>::eh;
leftRotate(lchildNode);
rightRotate(node);
break;
default:
break;
}
}
template<typename T>
void TreeAVL<T>::rightBalance(TreeNodeAVL<T> * node)
{
// 调用此函数时,node->m_bf == RH,即右边高于左边
TreeNodeAVL<T> * rchildNode = (TreeNodeAVL<T> * )node->rchild, * tempNode = nullptr;
switch (rchildNode->m_bf)
{
case TreeNodeAVL<T>::eh:// 平衡因子
node->m_bf = TreeNodeAVL<T>::rh;
rchildNode->m_bf = TreeNodeAVL<T>::lh;
leftRotate(node);
break;
case TreeNodeAVL<T>::lh:// 和有孩子的平衡因子不同,则先以右孩子为中心右旋,然后在左旋
// 右旋会影响到右孩子的左孩子的平衡因子
tempNode = (TreeNodeAVL<T> * )rchildNode->lchild;
switch (tempNode->m_bf)
{
case TreeNodeAVL<T>::eh:
node->m_bf = TreeNodeAVL<T>::eh;
rchildNode->m_bf = TreeNodeAVL<T>::eh;
break;
case TreeNodeAVL<T>::lh:
node->m_bf = TreeNodeAVL<T>::eh;
rchildNode->m_bf = TreeNodeAVL<T>::rh;
break;
case TreeNodeAVL<T>::rh:
node->m_bf = TreeNodeAVL<T>::lh;
rchildNode->m_bf = TreeNodeAVL<T>::eh;
break;
default:
break;
}
tempNode->m_bf = TreeNodeAVL<T>::eh;
rightRotate(rchildNode);
leftRotate(node);
break;
case TreeNodeAVL<T>::rh:// 和右孩子的平衡因子相同,直接左旋
node->m_bf = TreeNodeAVL<T>::eh;
rchildNode->m_bf = TreeNodeAVL<T>::eh;
leftRotate(node);
break;
default:
break;
}
}
删除操作
删除操作,并不复杂,和二叉查找树差不多。只是再删除后需要考虑以下要不要调整树结构,实现代码如下:
bool TreeAVL<T>::deleteElement(T key)
{
TreeNode<T> * pCurrent = search(key);
if (nullptr == pCurrent)
return false;
TreeNodeAVL<T> * pParent = (TreeNodeAVL<T> * )getParent(key);
bool delRight;
if (nullptr != pParent)
{
if (pCurrent == pParent->lchild)
{
delRight = false;
}
else
{
delRight = true;
}
}
if (nullptr == pCurrent->lchild)// 无左孩子
{
replaceAByB(pCurrent, pCurrent->rchild);
}
else if (nullptr == pCurrent->rchild)// 无右孩子
{
replaceAByB(pCurrent, pCurrent->lchild);
}
else
{
TreeNode<T> * pSuccesor = minElement(pCurrent->rchild);
replaceAByB(pSuccesor, pSuccesor->rchild);// 后继者是没有左孩子的
pSuccesor->rchild = pCurrent->rchild;
pSuccesor->lchild = pCurrent->lchild;
replaceAByB(pCurrent, pSuccesor);
}
delete pCurrent;
pCurrent = nullptr;
// 修改相关平衡因子
if (nullptr != pParent)
{
if (delRight)// 如果删除的是右孩子
{
switch (pParent->m_bf)
{
case TreeNodeAVL<T>::lh:// 本来左高,删除右孩子后就需要左平衡了
leftBalance(pParent);
break;
case TreeNodeAVL<T>::eh:
pParent->m_bf = TreeNodeAVL<T>::lh;
break;
case TreeNodeAVL<T>::rh:
pParent->m_bf = TreeNodeAVL<T>::eh;
break;
default:
break;
}
}
else// 如果删除的是左孩子
{
switch (pParent->m_bf)
{
case TreeNodeAVL<T>::lh:
pParent->m_bf = TreeNodeAVL<T>::eh;
break;
case TreeNodeAVL<T>::eh:
pParent->m_bf = TreeNodeAVL<T>::rh;
break;
case TreeNodeAVL<T>::rh:// 本来右高,删除左孩子后就需要右平衡了
rightBalance(pParent);
break;
default:
break;
}
}
}
}
平衡二叉树的完整实现
这里把平衡二叉树封装为一个类,继承自前面提到的二叉搜索树,如下:
// 平衡二叉树(AVL)
template<typename T>
class TreeNodeAVL : public TreeNode<T>
{
public:
enum {eh, lh, rh} m_bf;// 分别为等高,左高,右高
TreeNodeAVL() : m_bf(eh) {}
};
template<typename T>
class TreeAVL : public TreeBST<T>
{
public:
TreeAVL() : m_taller(true) {}
virtual bool insert(T key);
virtual bool deleteElement(T key);
protected:
virtual bool insertElement(TreeNodeAVL<T> *& node, T key);
void leftBalance(TreeNodeAVL<T> * node);
void rightBalance(TreeNodeAVL<T> * node);
void leftRotate(TreeNodeAVL<T> * node);
void rightRotate(TreeNodeAVL<T> * node);
bool m_taller;// 元素是否增加
};
template<typename T>
bool TreeAVL<T>::insert(T key)
{
return insertElement((TreeNodeAVL<T> *&)m_root, key);
}
template<typename T>
bool TreeAVL<T>::insertElement(TreeNodeAVL<T> *& node, T key)
{
if (nullptr == node)// 空树
{
node = new TreeNodeAVL<T>;
node->data = key;
node->m_bf = TreeNodeAVL<T>::eh;
m_taller = true; // 元素增加
}
else if (key == node->data)// 已经有该关键字
{
m_taller = false; // 元素未增加
return false;
}
else
{
if (key < node->data)
{
if (!insertElement((TreeNodeAVL<T> *&)node->lchild, key))
return false;
if (m_taller)// 元素增加要修正平衡因子
{
switch (node->m_bf)
{
case TreeNodeAVL<T>::eh:
node->m_bf = TreeNodeAVL<T>::lh;
m_taller = true; // 上层也要响应的修改平衡因子
break;
case TreeNodeAVL<T>::lh:
leftBalance(node);// 说明现在已经是左边过高了,所以要平衡
m_taller = false; // 已经经过平衡,则不用再平衡
break;
case TreeNodeAVL<T>::rh:
node->m_bf = TreeNodeAVL<T>::eh; // 因为原来右高,现在左边添加一元素,则平衡
m_taller = false;
break;
default:
break;
}
}
}
else
{
if (!insertElement((TreeNodeAVL<T> *&)node->rchild, key))
return false;
if (m_taller)// 元素增加要修正平衡因子
{
switch (node->m_bf)
{
case TreeNodeAVL<T>::eh:
node->m_bf = TreeNodeAVL<T>::rh;
m_taller = true; // 上层也要响应的修改平衡因子
break;
case TreeNodeAVL<T>::lh:
node->m_bf = TreeNodeAVL<T>::eh; // 因为原来左高,现在左边添加一元素,则平衡
m_taller = false;
break;
case TreeNodeAVL<T>::rh:
rightBalance(node); // 说明现在已经是右边过高了,所以要平衡
m_taller = false; // 已经经过平衡,则不用再平衡
break;
default:
break;
}
}
}
}
return true;
}
template<typename T>
bool TreeAVL<T>::deleteElement(T key)
{
TreeNode<T> * pCurrent = search(key);
if (nullptr == pCurrent)
return false;
TreeNodeAVL<T> * pParent = (TreeNodeAVL<T> * )getParent(key);
bool delRight;
if (nullptr != pParent)
{
if (pCurrent == pParent->lchild)
{
delRight = false;
}
else
{
delRight = true;
}
}
if (nullptr == pCurrent->lchild)// 无左孩子
{
replaceAByB(pCurrent, pCurrent->rchild);
}
else if (nullptr == pCurrent->rchild)// 无右孩子
{
replaceAByB(pCurrent, pCurrent->lchild);
}
else
{
TreeNode<T> * pSuccesor = minElement(pCurrent->rchild);
replaceAByB(pSuccesor, pSuccesor->rchild);// 后继者是没有左孩子的
pSuccesor->rchild = pCurrent->rchild;
pSuccesor->lchild = pCurrent->lchild;
replaceAByB(pCurrent, pSuccesor);
}
delete pCurrent;
pCurrent = nullptr;
// 修改相关平衡因子
if (nullptr != pParent)
{
if (delRight)// 如果删除的是右孩子
{
switch (pParent->m_bf)
{
case TreeNodeAVL<T>::lh:// 本来左高,删除右孩子后就需要左平衡了
leftBalance(pParent);
break;
case TreeNodeAVL<T>::eh:
pParent->m_bf = TreeNodeAVL<T>::lh;
break;
case TreeNodeAVL<T>::rh:
pParent->m_bf = TreeNodeAVL<T>::eh;
break;
default:
break;
}
}
else// 如果删除的是左孩子
{
switch (pParent->m_bf)
{
case TreeNodeAVL<T>::lh:
pParent->m_bf = TreeNodeAVL<T>::eh;
break;
case TreeNodeAVL<T>::eh:
pParent->m_bf = TreeNodeAVL<T>::rh;
break;
case TreeNodeAVL<T>::rh:// 本来右高,删除左孩子后就需要右平衡了
rightBalance(pParent);
break;
default:
break;
}
}
}
}
template<typename T>
void TreeAVL<T>::leftBalance(TreeNodeAVL<T> * node)
{
// 调用此函数时,node->m_bf == RH,即右边高于左边
TreeNodeAVL<T> * lchildNode = (TreeNodeAVL<T> * )node->lchild, * tempNode = nullptr;
switch (lchildNode->m_bf)
{
case TreeNodeAVL<T>::eh:// 平衡因子
node->m_bf = TreeNodeAVL<T>::lh;
lchildNode->m_bf = TreeNodeAVL<T>::rh;
rightRotate(node);
break;
case TreeNodeAVL<T>::lh:// 和左孩子平衡因子相同
node->m_bf = TreeNodeAVL<T>::eh;
lchildNode->m_bf = TreeNodeAVL<T>::eh;
rightRotate(node);
break;
case TreeNodeAVL<T>::rh:// 和左孩子的平衡因子不同,则先以左孩子为中心左旋旋,然后再右旋
tempNode = (TreeNodeAVL<T> * )lchildNode->rchild;// 右旋会影响到左孩子的右孩子的平衡因子
switch (tempNode->m_bf)
{
case TreeNodeAVL<T>::eh:
node->m_bf = TreeNodeAVL<T>::eh;
lchildNode->m_bf = TreeNodeAVL<T>::eh;
break;
case TreeNodeAVL<T>::lh:
node->m_bf = TreeNodeAVL<T>::eh;
lchildNode->m_bf = TreeNodeAVL<T>::rh;
break;
case TreeNodeAVL<T>::rh:
node->m_bf = TreeNodeAVL<T>::lh;
lchildNode->m_bf = TreeNodeAVL<T>::eh;
break;
default:
break;
}
tempNode->m_bf = TreeNodeAVL<T>::eh;
leftRotate(lchildNode);
rightRotate(node);
break;
default:
break;
}
}
template<typename T>
void TreeAVL<T>::rightBalance(TreeNodeAVL<T> * node)
{
// 调用此函数时,node->m_bf == RH,即右边高于左边
TreeNodeAVL<T> * rchildNode = (TreeNodeAVL<T> * )node->rchild, * tempNode = nullptr;
switch (rchildNode->m_bf)
{
case TreeNodeAVL<T>::eh:// 平衡因子
node->m_bf = TreeNodeAVL<T>::rh;
rchildNode->m_bf = TreeNodeAVL<T>::lh;
leftRotate(node);
break;
case TreeNodeAVL<T>::lh:// 和有孩子的平衡因子不同,则先以右孩子为中心右旋,然后在左旋
// 右旋会影响到右孩子的左孩子的平衡因子
tempNode = (TreeNodeAVL<T> * )rchildNode->lchild;
switch (tempNode->m_bf)
{
case TreeNodeAVL<T>::eh:
node->m_bf = TreeNodeAVL<T>::eh;
rchildNode->m_bf = TreeNodeAVL<T>::eh;
break;
case TreeNodeAVL<T>::lh:
node->m_bf = TreeNodeAVL<T>::eh;
rchildNode->m_bf = TreeNodeAVL<T>::rh;
break;
case TreeNodeAVL<T>::rh:
node->m_bf = TreeNodeAVL<T>::lh;
rchildNode->m_bf = TreeNodeAVL<T>::eh;
break;
default:
break;
}
tempNode->m_bf = TreeNodeAVL<T>::eh;
rightRotate(rchildNode);
leftRotate(node);
break;
case TreeNodeAVL<T>::rh:// 和右孩子的平衡因子相同,直接左旋
node->m_bf = TreeNodeAVL<T>::eh;
rchildNode->m_bf = TreeNodeAVL<T>::eh;
leftRotate(node);
break;
default:
break;
}
}
template<typename T>
void TreeAVL<T>::leftRotate(TreeNodeAVL<T> * node)
{
TreeNodeAVL<T> temp;// 临时对象,用来中转根节点
TreeNodeAVL<T> * rchild = (TreeNodeAVL<T> * )node->rchild;
temp = * node;
// 构建根节点
* node = * (TreeNodeAVL<T> * )(node->rchild);
// 构建根节点的左孩子节点
temp.rchild = rchild->lchild;
* rchild = temp;
node->lchild = rchild;
}
template<typename T>
void TreeAVL<T>::rightRotate(TreeNodeAVL<T> * node)
{
TreeNodeAVL<T> temp;// 临时对象,用来中转根节点
TreeNodeAVL<T> * lchild = (TreeNodeAVL<T> * )node->lchild;
temp = * node;
// 构建根节点
* node = * (TreeNodeAVL<T> * )(node->lchild);
// 构建根节点的有孩子节点
temp.lchild = lchild->rchild;
* lchild = temp;
node->rchild = lchild;
}
测试代码如下:
int main()
{
int ptr[10] = { 3, 2, 1, 4, 5, 6, 7, 10, 9, 8 };
TreeAVL<int> tree;
for (int i = 0; i < 10; i++)
{
tree.insert(ptr[i]);
}
// 中序遍历
std::cout << "中序遍历" << std::endl;
tree.inOrderTraverse();
// 前序遍历
std::cout << "前序遍历" << std::endl;
tree.preOrderTraverse();
getchar();
return 0;
}
测试结果如下:
测试代码中的序列,就是前面举的例子中的树。输出了前序遍历和中序遍历。我们知道知道一颗二叉树的前序遍历后中序遍历是可以确定一颗树的。输出结果是与我们之前推导出的树结构是一致的。感兴趣的可以用中序遍历和后续遍历的结果恢复一下,与前面的树结构对照以下。