一、什么是二叉树
在计算机科学中,二叉树是每个节点最多有两个子树的树结构,通常子树被称为左子树和右子树,左右子树又是一个二叉树,其次序不能任意颠倒。
二、二叉树的相关操作
1、二叉树结点的定义
二叉树的存储结构一般采用二叉链表的方式,每一个结点都有一个数据域_data和指向左右子树的两个指针域,通过这两个指针域就可以建立二叉树中的上下层关系。下面就是二叉树结点的定义:
template<class T>
struct BinTreeNode
{
BinTreeNode()
{}
BinTreeNode(T data) //构造函数
:_left(NULL)
, _right(NULL)
, _data(data)
{}
BinTreeNode*_left; //左指针域
BinTreeNode*_right; //右指针域
T _data; //数据域
};
2、二叉树的创建
首先我们要创建一个形如下面的二叉树,对应的输入数组为“char a[]=”ABD###CE##F”,即按照先序遍历的方法实现,’#’代表为空:
代码实现模块:
// 根+左子树+右子树
Node* _CreateBinTree( T* array, size_t size, size_t& index, const T& invalid)
{
Node *_pRoot = NULL;
if (invalid == array[index])//判断根节点是否为空
return _pRoot;
if (index < size && array[index] != invalid)
{
_pRoot = new BinTreeNode<T>(array[index]);//创建根节点
_pRoot->_left = _CreateBinTree(array, size, ++index, invalid);//创建左子树
_pRoot->_right = _CreateBinTree(array, size, ++index, invalid);//创建右子树
}
return _pRoot;
}
3、二叉树遍历
(1) 先序遍历(递归实现):根节点–>左子树—>右子树
void PreOrder()
{
_PreOrder(_pRoot);
}
void _PreOrder(PNode _pRoot)
{
if (NULL == _pRoot)//判空
return;
else
{
cout << _pRoot->_data << " ";//访问根节点
_PreOrder(_pRoot->_left);//遍历左子树
_PreOrder(_pRoot->_right);//遍历右子树
}
非递归实现先序遍历:
void PreOrder_Nor()//先序遍历--循环
{
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->_right)
s.push(pCur->_right);//保存当前结点的右子树
pCur = pCur->_left;//保存当前结点的左子树
}
}
cout << endl;
}
(2)中序遍历递归实现:左子树–>访问根节点–>右子树
void InOrder()
{
_InOrder(_pRoot);
}
void _InOrder(PNode _pRoot)
{
if (NULL == _pRoot)
return;
else
{
_InOrder(_pRoot->_left);//遍历左子树
cout << _pRoot->_data << " ";//访问根节点
_InOrder(_pRoot->_right);//遍历右子树
}
}
非递归实现:
void InOrder_Nor()
{
if (NULL == _pRoot)
return;
stack<PNode>s;
PNode pCur = _pRoot;
while (pCur || !s.empty())
{
//保存左侧路径的所有节点
while (pCur)
{
s.push(pCur);
pCur = pCur->_left;
}
pCur = s.top();//取栈顶元素并访问
cout << pCur->_data << " ";
s.pop();
pCur = pCur->_right;//如果pCur->_left为空,将pCur->_right作为当前节点,重复以上操作
}
cout << endl;
}
(3)后序遍历递归实现:左子树–>右子树–>根节点
void PostOrder()
{
_PostOrder(_pRoot);
}
void _PostOrder(PNode _pRoot)//后序遍历
{
if (NULL == _pRoot)//判空
return;
else
{
_PostOrder(_pRoot->_left);//先遍历左子树
_PostOrder(_pRoot->_right);//遍历右子树
cout << _pRoot->_data << " ";//打印根节点
}
}
非递归实现:
void PostOrder_Nor()
{
stack<PNode>s;//存放所有节点
PNode pCur = _pRoot;
PNode prev = NULL;
while (pCur || !s.empty())
{
while (pCur&&pCur != prev) //存放左侧路径所有节点
{
s.push(pCur); //当左子树未被标记时入栈
pCur = pCur->_left;
}
PNode pTop = s.top();
if (NULL == pTop->_right || prev == pTop->_right)//如果栈顶元素的右子树为空,并且已经被访问过
{
cout << pTop->_data << " "; //则输出该栈顶元素并出栈
prev = pTop;
s.pop();
if (!s.empty())
pCur = s.top();
else
return; //当栈为空时,退出
}
pCur = pCur->_right;
}
}
(4)层序遍历
需要借助队列先进先出的特性,主要有以下几个步骤:
a、定义一个队列用于存储根节点的指针
b、将根节点的指针入队列
c、当队列非空时:取队列队头结点并出队列,若该节点的左子树非空,该节点左子树入队列,若该节点的右子树非空,则该节点右子树入队列。
void _LevelOrder(PNode _pRoot)
{
queue<Node *> q;
q.push(_pRoot);
while (!q.empty())
{
Node*pCur = q.front();//取栈顶元素并访问
cout << pCur->_data << " ";
if (NULL == _pRoot)
return;
if (pCur->_left)//左子树入栈
{
q.push(pCur->_left);
}
if (pCur->_right)//右子树入栈
q.push(pCur->_right);
q.pop();//每当被访问过就pop出栈
}
}
4、判断是否为完全二叉树
由完全二叉树的定义可知,完全二叉树是指:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
代码实现过程如下:
bool IsCompleteBInTree()
{
if (NULL == _pRoot)//如果是空树,返回true
return true;
if (_pRoot->_left == NULL&&_pRoot->_right == NULL)
return true;
queue<PNode>q;
q.push(_pRoot);
bool flag = false; //用来标记是否需要对结点进行处理
while (!q.empty())
{
if (flag)
{
while (!q.empty())
{
PNode Front = q.front();
if (Front->_left == NULL&&Front->_right == NULL)// 若其他节点都为空,则
{
q.pop();
}
else
return false;
}
}
else
{
PNode Front = q.front();
q.pop();
if (Front->_left && Front->_right)//左右节点都不为空
{
q.push(Front->_left);
q.push(Front->_right);
}
else if (Front->_left)//左结点不为空,右节点为空
{
flag = true;
}
else if (Front->_right)//右节点不为空,左结点为空
return false;
else //左右节点都为空
{
flag = true;
}
}
}
return true;
}
5、二叉树的镜像转换
镜像转换即就是交换二叉树的左右子树,实现代码如下:
递归实现:
PNode MirrorTree()
{
return _MirrorTree(_pRoot);
}
PNode _MirrorTree(PNode pRoot)//镜像转换
{
if (NULL == pRoot)
return NULL;
if (pRoot->_left == NULL&&pRoot->_right == NULL)
return pRoot;
else
{
swap(pRoot->_left, pRoot->_right);
_MirrorTree(pRoot->_left);//递归镜像左子树
_MirrorTree(pRoot->_right);//递归镜像右子树
}
}
非递归实现(借助栈实现):
void _MirrorTree_Nor()
{
if (NULL == _pRoot)
return;
stack<PNode> q;
q.push(_pRoot);
while (!q.empty())
{
PNode pTop = q.top();
q.pop();
swap(pTop->_left, pTop->_right);//交换左右子树
if (pTop->_left)//遍历左子树
q.push(pTop->_left);
else if (pTop->_right)//遍历右子树
q.push(pTop->_right);
}
}
6、求二叉树叶子节点个数
递归实现:左子树叶子结点个数+右子树叶子结点个数
size_t GetLeefCount()//求叶子节点个数
{
return _GetLeefCount(_pRoot);
}
size_t _GetLeefCount(PNode _pRoot)
{
if (NULL == _pRoot)
return 0;
if (_pRoot->_left == NULL&&_pRoot->_right == NULL)
return 1;
return _GetLeefCount(_pRoot->_left) + _GetLeefCount(_pRoot->_right);
}
7、获取第K层节点个数
size_t GetKLevelCount(size_t K)
{
size_t index = 1;
return _GetKLevelCount(_pRoot,K, index);
}
size_t _GetKLevelCount(PNode pRoot,size_t K, size_t index)
{
if (NULL == pRoot)
return 0;
if (index < K - 1)
{
return _GetKLevelCount(pRoot->_left, K, index + 1) + _GetKLevelCount(pRoot->_right, K, index + 1);
}
if (index == K - 1)
{
return (pRoot->_left!=NULL) + (pRoot->_right!=NULL);
}
return 0;
}
8、求二叉树的深度
深度为左右子树深度最大值+1
size_t Height()
{
return _Height(_pRoot);
}
size_t _Height(PNode pRoot)
{
if (NULL == pRoot)
return 0;
return _Height(pRoot->_left) > _Height(pRoot->_right) ? _Height(pRoot->_left) + 1 : _Height(pRoot->_right) + 1;
}
9、查找指定数据
PNode Find(const T&data)
{
return _Find(_pRoot, data);
}
PNode _Find(PNode pRoot,const T& data)
{
if (NULL == pRoot)
return NULL;
if (pRoot->_data == data)
return pRoot;
//在左子树中进行查找
Node * ret = _Find(pRoot->_left,data);
if (ret != NULL)
return ret;
//在右子树中进行查找
else
{
Node*cur=_Find(pRoot->_right,data);
if (cur != NULL)
return cur;
}
return NULL;
}
10、查找双亲节点
若树为空或只有一个节点—>返回NULL;
否则遍历左子树查找;
遍历右子树查找;
若左右子树都没有要查找的数据,返回空;
PNode Parent(PNode pNode)
{
return _Parent(_pRoot, pNode);
}
PNode _Parent(PNode pRoot, PNode pNode)//查找双亲节点
{
if (NULL == pRoot)
return NULL;
if (pRoot->_left == pNode || pRoot->_right == pNode)
return pRoot;
else
{
Node*tmp = NULL;
if (tmp = _Parent(pRoot->_left, pNode))
return tmp;
if (tmp = _Parent(pRoot->_right, pNode))
return tmp;
}
}
11、查找指定节点的左孩子
PNode _LeftChild(PNode pNode)
{
if (NULL == pNode)
return;
return pNode->_left;
}
12、查找指定节点的右孩子
PNode _RightChild(PNode pNode)
{
if (NULL == pNode)
return;
return pNode->_right;
}
13、二叉树的复制
拷贝根节点
拷贝左子树
拷贝右子树
PNode _CopyBinTree(PNode pRoot)
{
if (NULL == pRoot)
return NULL;
//拷贝根节点
Node* pNewNode = new Node(pRoot->_data);
//拷贝左子树
if (pRoot->_left)
pNewNode->_left = _CopyBinTree(pRoot->_left);
//拷贝右子树
if (pRoot->_right)
pNewNode->_right = _CopyBinTree(pRoot->_right);
return pNewNode;
}
14、二叉树的销毁
void _DestroyBinTree(PNode pRoot)
{
while(pRoot != NULL)
{
if (pRoot->_left)
{
_DestroyBinTree(pRoot->_left);
pRoot->_left = NULL;
}
if (pRoot->_right)
{
_DestroyBinTree(pRoot->_right);
pRoot->_right = NULL;
}
if (pRoot != NULL)
{
free(pRoot);
pRoot = NULL;
}
}
}
15、测试代码
int main()
{
char a[] = "ABD###CE##F";
//char a[] = "ABDE#####CF";
BinTree<char> s(a,strlen(a),'#');
//s.InOrder();
//s.InOrder_Nor();
/*s.PreOrder();
s.PreOrder_Nor();*/
//s.PostOrder();
//s.PostOrder_Nor();
/*int ret = 0;
ret = s.IsCompleteBInTree();
if (ret == 0)
cout << "不是完全二叉树" << endl;
else
cout << "是完全二叉树" << endl;*/
/*s.PreOrder();
cout << endl;
s.MirrorTree();*/
s.PreOrder();
cout << endl;
s._MirrorTree_Nor();
s.PreOrder();
/*BinTree<char> s2(s);
BinTree<char>s1 = s;
s1.PreOrder();
s2.PreOrder();*/
//s.Leve1Order();
/*cout << endl;
cout << s.Size() << endl;
cout << s.GetLeefCount() << endl;
cout << s.GetKLevelCount(3) << endl;
cout << s.Height() << endl;
cout << s.Find('B')->_data<<endl;*/
/*cout<<s.Parent(s.Find('B'))->_data<<endl;
cout << s.LeftChild(s.Find('A'))->_data;*/
//BinTree<char>*cur = s.Parent()
system("pause");
return 0;
}