二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。
下面为大家总结了二叉树相关的性质以及面试题:
二叉树中有着一些非常重要的推论:
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,则无右孩子;
二叉树的结构:
template<class T>
struct BinTreeNode
{
BinTreeNode(const T& data)
:_pLeft(NULL)
,_pRight(NULL)
,_data(data)
{}
BinTreeNode<T>* _pLeft;
BinTreeNode<T>* _pRight;
T _data;
};
创建二叉树:
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);//创建右子树
}
}
二叉树的遍历:
二叉树由左子树+根+右子树构成,二叉树的遍历可以分为四种:前序,中序,后序层序遍历四种。
前序遍历:根+左子树+右子树,即:先访问根,在访问左子树,在访问右子树;
中序遍历:左子树+根+右子树,即:先访问左子树,在访问根,在访问右子树;
后序遍历:左子树+右子树+根,即:先访问左子树,在访问右子树,在访问根;
层序遍历:按层的顺序,一层一层的遍历。
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);
}
}
拷贝二叉树:
//拷贝二叉树
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 _DestroyBinTree(pNode pRoot)
{
//采用后序遍历的思想,否则 先销毁根节点,左右字数将找不到了
if (pRoot)
{
_DestroyBinTree(pRoot->_pLeft);//销毁左子树
_DestroyBinTree(pRoot->_pRight);//销毁右子树
delete pRoot;
pRoot = NULL;
}
}
层序遍历二叉树:
//层序遍历二叉树
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;
if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL)
return 1;
return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);
}
二叉树K层节点个数:
//二叉树的第K层节点个数
size_t _GetLevelCount(pNode pRoot, size_t 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)
{
//返回MAX边 + 1
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;
}
二叉树查找值为data的节点:
//二叉树查找值为data的节点
void _Find(pNode pRoot, const T& data)
{
if (NULL == pRoot)
return NULL;
if (pRoot->_data == data)
return pRoot;
pNodeb 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);
}
判断是否为完全二叉树:
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;
}
以上就是二叉树的基本操作,里面有的部分代码借助了栈队列以及模板的相关知识,由于本人的水平有限,有不足的地方欢迎大家批评指正!