树是数据结构中非常重要的一部分内容,尤其是二叉树。
什么是二叉树?
树是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
二叉树则是每个结点最多有两个子树的树,二叉树的度最多为2,。左侧子树的称为左子树,右侧子树的称为右子树。
二叉树中有着一些非常重要的推论:
1. 度为2的结点比度为0的结点少1个;
2. 具有n个结点的完全二叉树的深度k为log2(n+1)上取整;
3. 若规定根结点的层数为1,则非空二叉树第i层上最多有2^(i-1)个结点;
4. 若规定只有根结点的二叉树深度为1,则深度为k的二叉树最多有2^k-1个结点;
5. 若对于具有n个结点的完全二叉树,从上至下,从左至右排序依次从0开始,那么对于序号i:
(1)若i>0序号为i的结点的双亲结点序号为(i-1)/2;
(2)若2*i+1<n,则序号为i的结点左孩子结点序号为2*i+1;若2*i+1>=n,则无左孩子;
(3)若2*i+1<n,则序号为i的结点右孩子结点序号为2*i+2;若2*i+1>=n,则无右孩子;
二叉树的结构
struct BinTreeNode
{
BinTreeNode(const T& data)
: _pLeft(NULL)
, _pRight(NULL)
, _data(data)
{}
BinTreeNode<T>* _pLeft;
BinTreeNode<T>* _pRight;
T _data;
};
二叉树的遍历:二叉树由左子树+根+右子树构成,二叉树的遍历可以分为四种:前序,中序,后序层序遍历四种。
前序遍历:根+左子树+右子树,即:先访问根,在访问左子树,在访问右子树;
中序遍历:左子树+根+右子树,即:先访问左子树,在访问根,在访问右子树;
后序遍历:左子树+右子树+根,即:先访问左子树,在访问右子树,在访问根;
层序遍历:按层的顺序,一层一层的遍历。
void _PreOrder(pNode pRoot)//前序
{
if (pRoot)
{
cout << pRoot->_data << " ";
_PreOrder(pRoot->_pLeft);
_PreOrder(pRoot->_pRight);
}
}
void _InOrder(pNode pRoot)//中序
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << pRoot->_data << " ";
_InOrder(pRoot->_pRight);
}
}
void _PostOrder(pNode pRoot)//后序
{
if (pRoot)
{
_PostOrder(pRoot->_pLeft);
_PostOrder(pRoot->_pRight);
cout << pRoot->_data << " ";
}
}
void _LevelOrder(pNode pRoot)//层序:层序的实现较为复杂,需要借助到队列,创建队列,将根结点入队,取队头元素
{ //访问队头元素并出队列,如果左子树不为空,则保存左子树结点,保存右子树结点。
if (NULL == pRoot)
return;
queue<pNode> q;
q.push(pRoot);
while (!q.empty())
{
pNode pCur = q.front();
cout << pCur->_data << " ";
q.pop();
if (pCur->_pLeft)
q.push(pCur->_pLeft);
if (pCur->_pRight)
q.push(pCur->_pRight);
}
}
二叉树的创建:我们可以按照前序遍历的思想,先创建根结点,在创建左子树,在创建右子树;在创建的过程中,为了方便创建,我们将‘#’作为无效的标志,当结点无左孩子或右孩子时我们添加一个‘#’。
void _CreateBinTree(pNode& pRoot, const T* arr, size_t size, size_t& index, const T& invalid)
{ index必须传引用,否则函数调用完,index变回原来的值
if (index < size && '#' != arr[index])//先后次序,首先不能越界,再判断是否为有效值,越界时二叉树已创立完成,不需要在判断是否为有效值了
{
pRoot = new Node(arr[index]);
_CreateBinTree(pRoot->_pLeft, arr, size, ++index, invalid);
_CreateBinTree(pRoot->_pRight, arr, size, ++index, invalid);
}
}
下面我们还要计算二叉树的结点的个数,叶子结点的个数,第K层结点的个数,树的高度,查找值为data的结点,查找双亲结点,查找孩子节点,求一个二叉树的镜像以及判断一个树是不是完全二叉树。
template <class T>
class BinTree
{
typedef BinTreeNode<T>* pNode;
typedef BinTreeNode<T> Node;
public:
//构造函数 拷贝构造 析构函数///
BinTree()
: _pRoot(NULL)
{}
BinTree(const T*arr, size_t size, const T& invalid)//构造函数
{
size_t index = 0;
_CreateBinTree(_pRoot, arr, size, index, invalid);//创建二叉树
}
BinTree(const BinTree<T>& bt)
{
_pRoot = _CopyBinTree(bt._pRoot);
}
BinTree& operator=(const BinTree& bt)//赋值运算符重载
{
if (this != &bt)
{
_DesrtoryBinTree(_pRoot);//当前树可能不为空,因此先要销毁原树
_pRoot = _CopyBinTree(bt._pRoot);
}
return *this;
}
~BinTree()//析构函数
{
_DesrtoryBinTree(_pRoot);
}
遍历函数/
void PreOrder()//前序
{
_PreOrder(_pRoot);
cout << endl;
}
void PreOrder_N1()//非递归遍历
{
if (NULL == _pRoot)
return;
stack<pNode> s;
s.push(_pRoot);
while (!s.empty())
{
pNode pTop = s.top();
cout << pTop->_data << " ";
s.pop();
if (pTop->_pRight)
s.push(pTop->_pRight);
if (pTop->_pLeft)
s.push(pTop->_pLeft);
}
cout << endl;
}
void PreOrder_N2()
{
if (NULL == _pRoot)
return;
stack<pNode> s;
s.push(_pRoot);
while (!s.empty())
{
pNode pCur = s.top();
s.pop();
while (pCur)
{
cout << pCur->_data << " ";
if (pCur->_pRight)
s.push(pCur->_pRight);
pCur = pCur->_pLeft;
}
}
cout << endl;
}
void InOrder()//中序
{
_InOrder(_pRoot);
cout << endl;
}
void InOrder_N()//非递归遍历
{
if (NULL == _pRoot)
return;
stack<pNode> s;
pNode pCur = _pRoot;
while (pCur || !s.empty())
{
while (pCur)//找到最左侧的结点
{
s.push(pCur);
pCur = pCur->_pLeft;
}
pCur = s.top();
cout << pCur->_data << " ";
s.pop();
pCur = pCur->_pRight;//将右子树也是一个树
}
cout << endl;
}
void PostOrder()//后序
{
_PostOrder(_pRoot);
cout << endl;
}
void PostOrder_N()//非递归遍历
{
if (NULL == _pRoot)
return;
pNode pCur = _pRoot;
pNode prev = NULL;
stack<pNode> s;
while (pCur || !s.empty())
{
while (pCur)
{
s.push(pCur);
pCur = pCur->_pLeft;
}
pNode pTop = s.top();
if (pTop->_pRight == NULL || prev == pTop->_pRight)
{
cout << pTop->_data << " ";
prev = pTop;
s.pop();
}
else
pCur = pTop->_pRight;
}
cout << endl;
}
void LevelOrder()//层序
{
_LevelOrder(_pRoot);
cout << endl;
}
//结点个数,高度等///
size_t Size()//结点个数
{
return _Size(_pRoot);
}
size_t GetLeafCount()//叶子结点个数
{
return _GetLeafCount(_pRoot);
}
size_t GetKLevelCount(size_t K)//获取第K层结点的个数
{
return _GetLevelCount(_pRoot, K);
}
size_t Height()//树的高度
{
return _Height(_pRoot);
}
pNode Find(const T& data)//查找值为data的结点
{
return _Find(_pRoot, data);
}
pNode Parent(pNode PNode)//找双亲结点
{
return _Parent(_pRoot, PNode);
}
pNode LeftChild(pNode PNode)//找左孩子
{
return (NULL == PNode) ? NULL : PNode->_pLeft;
}
pNode RightChild(pNode PNode)//找右孩子
{
return (NULL == PNode) ? NULL : PNode->_pRight;
}
void Mirror()//递归镜像
{
_Mirror(_pRoot);
}
void Mirror_N()//非递归镜像
{
if (NULL == _pRoot)
return;
queue<pNode> q;
q.push(_pRoot);
while (!q.empty())
{
pNode pCur = q.front();
swap(pCur->_pLeft, pCur->_pRight);
q.pop();
if (pCur->_pLeft)
q.push(pCur->_pLeft);
if (pCur->_pRight)
q.push(pCur->_pRight);
}
}
bool IsCompleteBinTree()//判断是否为完全二叉树
{
if (NULL == _pRoot)
return true;
queue<pNode> q;
q.push(_pRoot);
bool flag = false;
pNode pCur = NULL;
while (!q.empty())
{
pCur = q.front();//分为四种,当左右都不存在,保存下来,当左存在右不存在,将flag改为true,再判断
//当左不存在右存在,一定不是完全二叉树,当左右都不存在,改为true,继续判断
if (flag && (pCur->_pLeft || pCur->_pRight))
{
return false;//当pCur为不饱和结点的时候,flag为true,此时pCur若左孩子或右孩子都不是完全二叉树
}
else
{
if (pCur->_pLeft && pCur->_pRight)
{
q.push(pCur->_pLeft);
q.push(pCur->_pRight);
}
else if (pCur->_pLeft)//不饱和结点
{
q.push(pCur->_pLeft);
flag = true;
}
else if (pCur->_pRight)
return false;
else//不饱和结点
flag = true;
}
q.pop();
}
return true;
}
private:
void _CreateBinTree(pNode& pRoot, const T* arr, size_t size, size_t& index, const T& invalid)//创建二叉树
{ //index必须传引用,否则函数调用完,index变回原来的值 不能引用常量
if (index < size && invalid != arr[index])//先后次序,首先不能越界
{
pRoot = new Node(arr[index]);//创建根结点
_CreateBinTree(pRoot->_pLeft, arr, size, ++index, invalid);//创建左子树
_CreateBinTree(pRoot->_pRight, arr, size, ++index, invalid);//创建右子树
}
}
pNode _CopyBinTree(pNode pRoot)//拷贝二叉树
{
pNode pNewRoot = NULL;
if (pRoot)
{
pNewRoot = new Node(pRoot->_data);//拷贝根节点
if (pRoot->_pLeft)//左子树不存在就不用拷了
pNewRoot->_pLeft = _CopyBinTree(pRoot->_pLeft);//拷贝左子树
if (pRoot->_pRight)
pNewRoot->_pRight = _CopyBinTree(pRoot->_pRight);//拷贝右子树
}
return pNewRoot;
}
void _DesrtoryBinTree(pNode pRoot)//销毁二叉树
{//采用后序遍历的思想,否则,先销毁根节点,左右子树将找不到了
if (pRoot)
{
_DesrtoryBinTree(pRoot->_pLeft);//销毁左子树
_DesrtoryBinTree(pRoot->_pRight);//销毁右子树
delete pRoot;//销毁根节点
pRoot = NULL;
}
}
void _PreOrder(pNode pRoot)//前序递归
{
if (pRoot)
{
cout << pRoot->_data << " ";
_PreOrder(pRoot->_pLeft);
_PreOrder(pRoot->_pRight);
}
}
void _InOrder(pNode pRoot)//中序递归
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << pRoot->_data << " ";
_InOrder(pRoot->_pRight);
}
}
void _PostOrder(pNode pRoot)//后序递归
{
if (pRoot)
{
_PostOrder(pRoot->_pLeft);
_PostOrder(pRoot->_pRight);
cout << pRoot->_data << " ";
}
}
void _LevelOrder(pNode pRoot)//中序
{
if (NULL == pRoot)
return;
queue<pNode> q;
q.push(pRoot);
while (!q.empty())
{
pNode pCur = q.front();
cout << pCur->_data << " ";
q.pop();
if (pCur->_pLeft)
q.push(pCur->_pLeft);
if (pCur->_pRight)
q.push(pCur->_pRight);
}
}
size_t _Size(pNode pRoot)//结点个数
{//左子树结点个数+右子树结点个数+1
if (pRoot == NULL)
return 0;
return _Size(pRoot->_pLeft) + _Size(pRoot->_pRight) + 1;
}
size_t _GetLeafCount(pNode pRoot)//叶子结点个数
{//左子树叶子结点个数+右子树叶子结点个数
if (pRoot == NULL)
return 0;
if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL)
return 1;
return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);
}
size_t _GetLevelCount(pNode pRoot, size_t K)//第K层
{
if (K == 0 || pRoot == NULL)//size_t是大于等于0的,不用考虑小于0
return 0;
if (K == 1)
return 1;
return _GetLevelCount(pRoot->_pLeft, K - 1) + _GetLevelCount(pRoot->_pRight, K - 1);
}
size_t _Height(pNode pRoot)//高度
{
if (pRoot == NULL)
return 0;
if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL)
return 1;
size_t leftNode = _Height(pRoot->_pLeft);
return _Height(pRoot->_pRight) > leftNode ? _Height(pRoot->_pRight) + 1 : leftNode + 1;
}
pNode _Find(pNode pRoot, const T& data)//查找值为data的结点
{
if (NULL == pRoot)
return NULL;
if (pRoot->_data == data)
return pRoot;
pNode tmp = _Find(pRoot->_pLeft, data);//先在根找,根没找到,再去左子树找,找到直接返回,没找到再去右子树
return (NULL == tmp) ? _Find(pRoot->_pRight, data) : tmp;
}
pNode _Parent(pNode pRoot, pNode PNode)//找双亲结点
{
if (NULL == pRoot || pRoot == PNode || PNode == NULL)
return NULL;
if (pRoot->_pLeft == PNode || pRoot->_pRight == PNode)
return pRoot;
pNode tmp = _Parent(pRoot->_pLeft, PNode);
return (NULL == tmp) ? _Parent(pRoot->_pRight, PNode) : tmp;
}
void _Mirror(pNode pRoot)//镜像
{
if (NULL == pRoot)
return;
if (pRoot->_pLeft || pRoot->_pRight)
swap(pRoot->_pLeft, pRoot->_pRight);
_Mirror(pRoot->_pLeft);
_Mirror(pRoot->_pRight);
}
private:
pNode _pRoot;
};
下面是我的测试函数:
void Test()
{
char* pStr = "abd###ce##f";
BinTree<char> bt(pStr, strlen(pStr), '#');
if (bt.IsCompleteBinTree())
cout << "是完全二叉树" << endl;
else
cout << "不是完全二叉树" << endl;
bt.PreOrder();
bt.Mirror();
bt.Mirror_N();
bt.PreOrder();
bt.PostOrder_N();
bt.PreOrder_N1();
bt.PreOrder_N2();
bt.InOrder_N();
bt.PreOrder();
bt.LevelOrder();
bt.InOrder();
bt.PostOrder();
cout << bt.Size() << endl;
cout << bt.GetLeafCount() << endl;
cout << bt.Height() << endl;
BinTreeNode<char>* pcur = bt.Parent(bt.Find('b'));
cout << pcur->_data << endl;
cout << bt.LeftChild(bt.Find('a'))->_data << endl;
cout << bt.LeftChild(bt.Find('b'))->_data << endl;
if (bt.RightChild(bt.Find('b')))
cout << bt.RightChild(bt.Find('b'))->_data << endl;
cout << bt.LeftChild(bt.Find('c'))->_data << endl;
cout << bt.RightChild(bt.Find('c'))->_data << endl;
cout << bt.GetKLevelCount(1) << endl;
cout << bt.GetKLevelCount(2) << endl;
cout << bt.GetKLevelCount(3) << endl;
BinTree<char> bt1(bt);
bt1.PreOrder();
BinTree<char> bt2;
bt2 = bt;
bt2.PreOrder();
}
这些函数基本都用到了递归+前序遍历或后序遍历的思想,有的借助到了栈或者队列,其实现方法并不复杂。