【数据结构】二叉树常见面试题
1、创建二叉树
树的结构如下:
这里我们创建二叉树用的是静态创建即不是每次从标准输入读入数据然后插入树中,而是将定义好的一个序列,也就是一个数组构建一棵树,这里采用的是递归创建,首先来看一下参数,首先传进来的就是根结点,然后是要构建树的序列然后是数组下标索引,负责遍历整个数组,然后是数组的大小,然后是无效值,遇到无效值表示当前结点为空,就返回上一层函数调用,递归程序的出口是index<size&&array[index]!=invalid
,因为数组下标是从0-size-1所以一旦到达size就该退出了,还有就是当前结点为空的时候也退出,递归具体就是,用数组元素创建一个结点,然后把它赋给根结点,然后去创建根的左子树,创建根的右子树,一直递归创建。
2、前/中/后遍历二叉树(递归&非递归)
前序递归遍历:
如果根不为空,就将根结点打印出来,然后前序访问它的左子树,访问它的右子树。
前序非递归遍历:
这里使用栈来实现,首先将根放进栈中然后定义一个循环,循环条件是栈不为空,首先将栈顶元素弹出,并用一个结点保存,如果栈顶的元素不是空,就输出栈顶元素的数据,然后看它的左右子树,先把右子树压入栈中,再压左子树,(因为栈后进先出,先要访问左子树,所以左子树后进入),每次进入循环如果栈顶为空的化就不进行操作,直接进行操作,如果栈顶不为空的话再进行下面操作。直到所有结点都被遍历。
中序递归遍历:
和前序类似,只是这里先中序遍历它的左子树,然后输出根结点然后递归遍历右子树。
中序非递归遍历:
还是利用栈,因为先访问最左边的结点,所有我们先定义一个循环一直向左走把左边的所有结点压入栈中,这时候栈顶就是最左侧得结点,我们要将这个接单弹出,然后输出它的值,下来输出的是它的根结点,所以先要把它的右子树都压入栈中,最后再保存根结点就能保证先输出根结点再输出它右侧元素了。
后序递归遍历:
和前两种递归遍历相似,就是顺序不一样,显示递归访问它的左子树,然后递归访问右子树,最后输出根结点。
后序非递归遍历:
还是利用栈,和中序非递归类似,先找到最左边的结点,并把这条路径上所有的结点按照找的顺序压入栈中,然后将最左侧结点的数据打印出来,然后判断当前节点是不是栈顶结点的左子树,如果是要先访问右结点才能访问根结点,否则将当前结点置成空,下一次就会输出当前结点的根结点,然后重复上述操作。
3、层序遍历二叉树
层序遍历使用了队列这个数据结构,层序遍历就是把二叉树从上到下,一层一层打印出来,我们先把头结点加入队列当中,然后定义一个循环,进去先将队列中的队头元素的内容打印出来,然后将头结点出队列,然后判断如果头结点的左孩子不为空,就把左孩子放进队列,右孩子不为空就把右孩子放进队列,然后进入下一次循环,继续打印队头元素,然后判断左右孩子,不为空的放进队列,直到队列为空。
4、求二叉树的高度
这里也采用递归的思想,如果根结点是空的话,高度就是0,返回一个0,如果不为空看一下左右子树的高度,因为整棵树的高度就是左右子树中高度的最大值,最后两个比较,返回高度最大的子树的高度加1,加1就是加上根结点自己。
5、求二叉树中结点的个数
进入函数后先判断根结点是不是空,是空的话返回0个,然后统计左子树的结点个数,右子树的节点个数,最后总的个数就等于左子树结点个数加上右子树结点个数加上根结点也就是1。
6、求叶子结点的个数
我们直到叶子结点的特点就是左右孩子都是空,我们进入函数先判断一下根是不是为空,是空的话直接返回0个就可以,如果不为空就判断根结点是不是叶子结点(左右子树为空)是的话直接返回1,不是的话,看一下左子树有多少个叶子结点,右子树有多少个叶子结点,最后加起来即可。
7、求二叉树中第k层结点的个数
如果二叉树为空或者k<0返回0
如果二叉树不为空并且k为1返回1
如果二叉树不为空并且k>1,返回左子树k-1层结点和右子树k-1层结点个数之和
8、判断一个节点是否在一棵二叉树中
如果树为空或者结点为空,返回false
如果根节点的数据等于当前节点的数据域,返回true
如果递归在它的左子树或者右子树任意一个能找到,返回true
否则返回false
9、求二叉树中两个结点的最近公共祖先结点
递归思路,如果两个结点一个在根节点的左子树,一个在根结点的右子树,它们的公共祖先就是根结点,如果两个都在左子树,就递归在根的左子树找
如果两个都在右子树,就递归在根的右子树找.
先将寻找两个节点的路径都用list保存起来然后定义一个迭代器来遍历这两个链表,当两个值相等的时候就是最近的公共祖先结点。
10、判断一棵二叉树是否是平衡二叉树
如果树为空将高度置为0,返回true,如果它的左右子树都是平衡二叉树,并且左右子树告诉之差小于等于1
11、求二叉树中最远的两个结点之间的距离
如果二叉树为空,返回0,同时记录左子树和右子树的深度,都为0
二叉树中最远的距离要么是左子树中的最大距离,要么是右子树中的最大距离,要么是左子树结点到根结点和右子树结点到根结点距离之和,同时记录左子树和右子树结点中到根结点的最大距离。
12、有前序遍历和中序遍历重建二叉树(前序遍历结果:
1,2,3,4,5,6 中序遍历结果:4,2,5,1,6,3)
如果前序遍历或中序遍历为空或者结点个数小于等于0,返回NULL
创建跟姐姐你到哪,前序便利的第一个就是根结点的数据,然后再在中序遍历中找到根结点位置,左侧就是左子树的数据,右侧就是右子树的数据,然后重建左右子树。
13、判断一棵二叉树是否是完全二叉树
由完全二叉树的定义直到,假如二叉树的深度是h,除了第h层外,其他层的结点个数都达到最大值,h层的结点个数都集中在最左边。
按层次遍历二叉树,当遇到一个结点的左子树为空则该结点的右子树必须为空,且后面的左右子树必须为空,否则不是完全二叉树。
因为是层序遍历的方式,就是从上到下,从左到右的顺序,判断也是按照这个顺序,可以设一个标志为来判断当前结点的下一个结点可以不可以有孩子,当前节点有左孩子它后面的结点都不能有孩子结点。
14、求二叉树的镜像
如果二叉树为空直接返回空,
如果二叉树不为空,求左子树的镜像和右子树的镜像,然后将左右子树交换。
15、将二叉搜索树转换成一个排序的双向链表。要求:不能
创建任何新的结点,只能调整树种结点指针的指向
如果二叉树为空,双向链表的第一个结点和最后一个结点都是NULL,
如果二叉树不为空:
如果左子树为空对应双向链表的第一个结点就是根结点,左边不需要任何操作
如果左子树不为空,转换左子树,二叉树对应双向链表的第一个结点就是左子树转换后双向链表的第一个结点,同时将根结点和左子树转换后的双向有序链表连接。
如果右子树为空,对应双向有序链表的最后一个结点是根结点,右边不需要任何操作,
如果右子树不为空,对应双向有序链表的最后一个结点就是右子树转换后双向有序链表的最后一个结点就是右子树转换后双向有序链表的最后一个结点,同时将根结点和右子树转换后的双向有序链表的第一个结点连接。
代码以及相关测试如下:
#include<iostream>
#include<stack>
#include<queue>
#include<list>
using namespace std;
template<class T>
struct BinTreeNode
{
struct BinTreeNode* _pLeft;
struct BinTreeNode* _pRight;
T _data;
BinTreeNode()
:_pLeft(NULL)
, _pRight(NULL)
{}
BinTreeNode(T& data)
:_pLeft(NULL)
, _pRight(NULL)
, _data(data)
{}
};
template<class T>
class BinTree
{
typedef struct BinTreeNode<T> node_t;
typedef node_t* pNode;
public:
BinTree()
:_pRoot(NULL)
{}
//1、创建二叉树
BinTree(T* array, int size, T invalid)
{
int index = 0;
_CreateBinTree(_pRoot, array, index, size, invalid);
}
//2、前 / 中 / 后遍历二叉树(递归&非递归)
void Preorder()
{
cout << "Preorder" << ":";
_Preorder(_pRoot);
cout << endl;
}
void Preordernonrecursive()
{
cout << "Preordernonrecursive" << ":";
_Preordernonrecursive(_pRoot);
cout << endl;
}
void Inorder()
{
cout << "Inorder" << ":";
_Inorder(_pRoot);
cout << endl;
}
void Inordernonrecursive()
{
cout << "Inordernonrecursive" << ":";
_Inordernonrecursive(_pRoot);
cout << endl;
}
void Postorder()
{
cout << "Postorder" << ":";
_Postorder(_pRoot);
cout << endl;
}
void Postordernonrecursive()
{
cout << "Postordernonrecursive" << ":";
_Postordernonrecursive(_pRoot);
cout << endl;
}
//3、层序遍历二叉树
void Levelorder()
{
cout << "Levelorder" << ":";
_Levelorder(_pRoot);
cout << endl;;
}
//4、求二叉树的高度
int Hight()
{
return _Hight(_pRoot);
}
//5、求二叉树中结点的个数
int count()
{
return _count(_pRoot);
}
//6、求叶子结点的个数
int LeafCount()
{
return _LeafCount(_pRoot);
}
//7、求二叉树中第k层结点的个数
int KLevelCount(size_t k)
{
return _KLevelCount(_pRoot,k);
}
//8、判断一个节点是否在一棵二叉树中
bool IsInBinTree(pNode Node)
{
return _IsInBinTree(_pRoot, Node);
}
//9、求二叉树中两个结点的最近公共祖先结点
pNode CurrentAncestor(pNode Node1,pNode Node2)
{
return _CurrentAncestor(_pRoot,Node1,Node2);
//return _CurrentAncestornonrecur(_pRoot, Node1, Node2);
}
//10、判断一棵二叉树是否是平衡二叉树
bool IsAVLTree()
{
int height;
return _IsAVLTree(_pRoot,height);
}
//11、求二叉树中最远的两个结点之间的距离
int MaxLengthNode2Node()
{
int maxleft , maxright ;
return _MaxLengthNode2Node(_pRoot,maxleft,maxright);
}
//12、有前序遍历和中序遍历重建二叉树(前序遍历结果:
pNode ReconstructBinTree(T Prearray[],T Inarray[],int nodenum)
{
return _ReconstructBinTree(Prearray, Inarray, nodenum);
}
//1, 2, 3, 4, 5, 6 中序遍历结果:4, 2, 5, 1, 6, 3)
//13、判断一棵二叉树是否是完全二叉树
bool IsCompleteBinTree()
{
return _IsCompleteBinTree(_pRoot);
}
//14、求二叉树的镜像
pNode MirrorBinTree()
{
return _MirrorBinTree(_pRoot);
}
//15、将二叉搜索树转换成一个排序的双向链表。要求:不能
//创建任何新的结点,只能调整树种结点指针的指向
void BinTree2List()
{
pNode pfirstNode, plastNode;
_BinTree2List(_pRoot,pfirstNode,plastNode);
}
~BinTree()
{
_DestroyBinTree(_pRoot);
}
private:
void _DestroyBinTree(pNode pRoot)
{
if (pRoot)
{
_DestroyBinTree(pRoot->_pLeft);
_DestroyBinTree(pRoot->_pRight);
delete pRoot;
pRoot = NULL;
}
}
//1、创建二叉树
/*这里我们创建二叉树用的是静态创建即不是每次从标准输入读入数据然后插入树中,而是将定义好的一个序列,也就是一个数组构建一棵树,这里采用的是递归创建,首先来看一下参数,首先传进来的就是根结点,然后是要构建树的序列然后是数组下标索引,负责遍历整个数组,然后是数组的大小,然后是无效值,遇到无效值表示当前结点为空,就返回上一层函数调用,递归程序的出口是index<size&&array[index] != invalid,因为数组下标是从0 - size - 1所以一旦到达size就该退出了,还有就是当前结点为空的时候也退出,递归具体就是,用数组元素创建一个结点,然后把它赋给根结点,然后去创建根的左子树,创建根的右子树,一直递归创建。*/
void _CreateBinTree(pNode &pRoot,T* array, int &index,int size, const T& invalid)
{
if (index<size&&array[index]!= invalid)
{
pRoot = new node_t(array[index]);
_CreateBinTree(pRoot->_pLeft, array, ++index, size, invalid);
_CreateBinTree(pRoot->_pRight, array, ++index, size, invalid);
}
}
/*2、前 / 中 / 后遍历二叉树(递归&非递归)
前序递归遍历:
如果根不为空,就将根结点打印出来,然后前序访问它的左子树,访问它的右子树。
前序非递归遍历:
这里使用栈来实现,首先将根放进栈中然后定义一个循环,循环条件是栈不为空,首先将栈顶元素弹出,并用一个结点保存,如果栈顶的元素不是空,就输出栈顶元素的数据,然后看它的左右子树,先把右子树压入栈中,再压左子树,(因为栈后进先出,先要访问左子树,所以左子树后进入),每次进入循环如果栈顶为空的化就不进行操作,直接进行操作,如果栈顶不为空的话再进行下面操作。直到所有结点都被遍历。
中序递归遍历:
和前序类似,只是这里先中序遍历它的左子树,然后输出根结点然后递归遍历右子树。
中序非递归遍历:
还是利用栈,因为先访问最左边的结点,所有我们先定义一个循环一直向左走把左边的所有结点压入栈中,这时候栈顶就是最左侧得结点,我们要将这个接单弹出,然后输出它的值,下来输出的是它的根结点,所以先要把它的右子树都压入栈中,最后再保存根结点就能保证先输出根结点再输出它右侧元素了。
后序递归遍历:
和前两种递归遍历相似,就是顺序不一样,显示递归访问它的左子树,然后递归访问右子树,最后输出根结点。
后序非递归遍历:
还是利用栈,和中序非递归类似,先找到最左边的结点,并把这条路径上所有的结点按照找的顺序压入栈中,然后将最左侧结点的数据打印出来,然后判断当前节点是不是栈顶结点的左子树,如果是要先访问右结点才能访问根结点,否则将当前结点置成空,下一次就会输出当前结点的根结点,然后重复上述操作。*/
void _Preorder(pNode pRoot)
{
if (pRoot)
{
cout << pRoot->_data << " ";
_Preorder(pRoot->_pLeft);
_Preorder(pRoot->_pRight);
}
}
void _Preordernonrecursive(pNode pRoot)
{
stack<pNode> s;
s.push(pRoot);
pNode p = NULL;
while (!s.empty())
{
p = s.top();
s.pop();
if (p != NULL)
{
cout << p->_data << " ";
s.push(p->_pRight);
s.push(p->_pLeft);
}
}
}
void _Inorder(pNode pRoot)
{
if (pRoot)
{
_Inorder(pRoot->_pLeft);
cout << pRoot->_data << " ";
_Inorder(pRoot->_pRight);
}
}
void _Inordernonrecursive(pNode pRoot)
{
stack<pNode> s;
pNode p = pRoot;
do
{
while (p)
{
s.push(p);
p = p->_pLeft;
}
p = s.top();
s.pop();
cout << p->_data << " ";
if (p->_pRight)
p = p->_pRight;
else
p = NULL;
} while (p != NULL || !s.empty());
}
void _Postorder(pNode pRoot)
{
if (pRoot)
{
_Postorder(pRoot->_pLeft);
_Postorder(pRoot->_pRight);
cout << pRoot->_data << " ";
}
}
void _Postordernonrecursive(pNode pRoot)
{
stack<pNode> s;
pNode p = pRoot;
while (p != NULL || !s.empty())
{
while (p)
{
s.push(p);
p = p->_pLeft;
}
p = s.top();
s.pop();
if (p)
cout << p->_data << " ";
if (!s.empty() && p == (s.top())->_pLeft)
{
p = (s.top())->_pRight;
}
else
p = NULL;
}
}
/*3、层序遍历二叉树
层序遍历使用了队列这个数据结构,层序遍历就是把二叉树从上到下,一层一层打印出来,我们先把头结点加入队列当中,然后定义一个循环,进去先将队列中的队头元素的内容打印出来,然后将头结点出队列,然后判断如果头结点的左孩子不为空,就把左孩子放进队列,右孩子不为空就把右孩子放进队列,然后进入下一次循环,继续打印队头元素,然后判断左右孩子,不为空的放进队列,直到队列为空*/
void _Levelorder(pNode pRoot)
{
if (pRoot == NULL)
return;
queue<pNode> q;
q.push(pRoot);
while (!q.empty())
{
pNode p = q.front();
q.pop();
cout << p->_data << " ";
if (p->_pLeft)
q.push(p->_pLeft);
if (p->_pRight)
q.push(p->_pRight);
}
}
//4、求二叉树的高度
/*这里也采用递归的思想,如果根结点是空的话,高度就是0,返回一个0,如果不为空看一下左右子树的高度,因为整棵树的高度就是左右子树中高度的最大值,最后两个比较,返回高度最大的子树的高度加1,加1就是加上根结点自己。*/
int _Hight(pNode pRoot)
{
if (pRoot==NULL)
return 0;
int LeftHight = _Hight(pRoot->_pLeft);
int RightHight = _Hight(pRoot->_pRight);
return LeftHight >= RightHight ? LeftHight +1 : RightHight+1 ;
}
//5、求二叉树中结点的个数
/*进入函数后先判断根结点是不是空,是空的话返回0个,然后统计左子树的结点个数,右子树的节点个数,最后总的个数就等于左子树结点个数加上右子树结点个数加上根结点也就是1。*/
int _count(pNode pRoot)
{
if (pRoot == NULL)
return 0;
int countLeft = _count(pRoot->_pLeft);
int countRight = _count(pRoot->_pRight);
return countLeft + countRight+1;
}
//6、求叶子结点的个数
/*我们直到叶子结点的特点就是左右孩子都是空,我们进入函数先判断一下根是不是为空,是空的话直接返回0个就可以,如果不为空就判断根结点是不是叶子结点(左右子树为空)是的话直接返回1,不是的话,看一下左子树有多少个叶子结点,右子树有多少个叶子结点,最后加起来即可。*/
int _LeafCount(pNode pRoot)
{
if (pRoot == NULL)
return 0;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)
return 1;
int countLeft = _LeafCount(pRoot->_pLeft);
int countRight = _LeafCount(pRoot->_pRight);
return countLeft + countRight;
}
/*7、求二叉树中第k层结点的个数
如果二叉树为空或者k<0返回0
如果二叉树不为空并且k为1返回1
如果二叉树不为空并且k>1,返回左子树k - 1层结点和右子树k - 1层结点个数之和*/
int _KLevelCount(pNode pRoot,size_t k)
{
if (pRoot == NULL || k < 0)
return 0;
if (k == 1)
return 1;
return (_KLevelCount(pRoot->_pLeft, k - 1) + _KLevelCount(pRoot->_pRight,k-1));
}
/*8、判断一个节点是否在一棵二叉树中
如果树为空或者结点为空,返回false
如果根节点的数据等于当前节点的数据域,返回true
如果递归在它的左子树或者右子树任意一个能找到,返回true
否则返回false*/
bool _IsInBinTree(pNode pRoot, pNode Node)
{
if (pRoot == NULL||Node == NULL)
return false;
if (pRoot->_data == Node->_data)
return true;
if (_IsInBinTree(pRoot->_pLeft, Node) || _IsInBinTree(pRoot->_pRight, Node))
return true;
return false;
}
/*9、求二叉树中两个结点的最近公共祖先结点
递归思路,如果两个结点一个在根节点的左子树,一个在根结点的右子树,它们的公共祖先就是根结点,如果两个都在左子树,就递归在根的左子树找
如果两个都在右子树,就递归在根的右子树找.
先将寻找两个节点的路径都用list保存起来然后定义一个迭代器来遍历这两个链表,当两个值相等的时候就是最近的公共祖先结点。*/
pNode _CurrentAncestor(pNode pRoot, pNode Node1, pNode Node2)
{
if (_IsInBinTree(pRoot->_pLeft, Node1))
{
if (_IsInBinTree(pRoot->_pRight, Node2))
return pRoot;
else
_CurrentAncestor(pRoot->_pLeft, Node1, Node2);
}
else
{
if (_IsInBinTree(pRoot->_pRight, Node1))
return pRoot;
else
_CurrentAncestor(pRoot->_pLeft, Node1, Node2);
}
}
bool GetNodePath(pNode pRoot, pNode Node, list<pNode> &path)
{
if (pRoot == Node)
{
path.push_back(pRoot);
return true;
}
if (pRoot == NULL)
return false;
path.push_back(pRoot);
bool found = false;
found = GetNodePath(pRoot->_pLeft, Node, path);
if (!found)
found = GetNodePath(pRoot->_pRight, Node, path);
if (!found)
path.pop_back();
return found;
}
pNode _CurrentAncestornonrecur(pNode pRoot, pNode Node1, pNode Node2)
{
if (pRoot == NULL || Node1 == NULL || Node2 == NULL)
return NULL;
list<pNode> path1;
bool ret1 = GetNodePath(pRoot, Node1, path1);
list<pNode> path2;
bool ret2 = GetNodePath(pRoot, Node2, path2);
if (!ret1 || !ret2)
return NULL;
pNode pLast = NULL;
list<pNode>::const_iterator iter1 = path1.begin();
list<pNode>::const_iterator iter2 = path2.begin();
while (iter1 != path1.end() && iter2 != path2.end())
{
if (*iter1 == *iter2)
pLast = *iter1;
else
break;
iter1++;
iter2++;
}
return pLast;
}
/*10、判断一棵二叉树是否是平衡二叉树
如果树为空将高度置为0,返回true,如果它的左右子树都是平衡二叉树,并且左右子树告诉之差小于等于1*/
bool _IsAVLTree(pNode pRoot,int &height)
{
if (pRoot == NULL)
{
height = 0;
return true;
}
int heightleft;
bool leftret = _IsAVLTree(pRoot->_pLeft, heightleft);
int heightright;
bool rightret = _IsAVLTree(pRoot->_pRight, heightright);
if (leftret&&rightret&&abs(heightleft - heightright) <= 1)
{
height = max(heightleft, heightright);
return true;
}
else
{
height = max(heightleft, heightright);
return false;
}
return false;
}
/*11、求二叉树中最远的两个结点之间的距离
如果二叉树为空,返回0,同时记录左子树和右子树的深度,都为0
二叉树中最远的距离要么是左子树中的最大距离,要么是右子树中的最大距离,要么是左子树结点到根结点和右子树结点到根结点距离之和,同时记录左子树和右子树结点中到根结点的最大距离。*/
int _MaxLengthNode2Node(pNode pRoot,int &maxleft,int &maxright)
{
if (pRoot == NULL)
{
maxleft = 0;
maxright = 0;
return 0;
}
int maxLL, maxLR, maxRL, maxRR;
int maxdisleft, maxdisright;
if (pRoot->_pLeft)
{
maxdisleft = _MaxLengthNode2Node(pRoot->_pLeft, maxLL, maxLR);
maxleft = max(maxLL, maxLR)+1;
}
else
{
maxdisleft = 0;
maxleft = 0;
}
if (pRoot->_pRight)
{
maxdisright = _MaxLengthNode2Node(pRoot->_pRight, maxRL, maxRR);
maxright = max(maxRL, maxRR)+1;
}
else
{
maxdisright = 0;
maxright = 0;
}
return max(max(maxdisleft, maxdisright), maxleft + maxright);
}
/*12、有前序遍历和中序遍历重建二叉树(前序遍历结果:
1, 2, 3, 4, 5, 6 中序遍历结果:4, 2, 5, 1, 6, 3)
如果前序遍历或中序遍历为空或者结点个数小于等于0,返回NULL
创建跟姐姐你到哪,前序便利的第一个就是根结点的数据,然后再在中序遍历中找到根结点位置,左侧就是左子树的数据,右侧就是右子树的数据,然后重建左右子树。*/
pNode _ReconstructBinTree(T Prearray[], T Inarray[],int nodenumber)
{
if (Prearray == NULL || Inarray == NULL || nodenumber <= 0)
return NULL;
pNode pRoot = new node_t(Prearray[0]);
int PosInInarray = -1;
for (int i = 0; i < nodenumber; i++)
{
if (Inarray[i]==pRoot->_data)
{
PosInInarray = i;
break;
}
}
if (PosInInarray == -1)
{
throw exception("Invalid input");
}
int nodenumleft = PosInInarray;
T *prearrayleft = Prearray + 1;
T *inarrayleft = Inarray;
pRoot ->_pLeft = _ReconstructBinTree(prearrayleft, inarrayleft, nodenumleft);
int nodenumright = nodenumber - nodenumleft - 1;
T *prearrayright = Prearray + 1 + nodenumleft;
T *inarrayright = Inarray + nodenumleft+1;
pRoot->_pRight = _ReconstructBinTree(prearrayright, inarrayright, nodenumright);
return pRoot;
}
/*13、判断一棵二叉树是否是完全二叉树
由完全二叉树的定义直到,假如二叉树的深度是h,除了第h层外,其他层的结点个数都达到最大值,h层的结点个数都集中在最左边。
按层次遍历二叉树,当遇到一个结点的左子树为空则该结点的右子树必须为空,且后面的左右子树必须为空,否则不是完全二叉树。
因为是层序遍历的方式,就是从上到下,从左到右的顺序,判断也是按照这个顺序,可以设一个标志为来判断当前结点的下一个结点可以不可以有孩子,当前节点有左孩子它后面的结点都不能有孩子结点。*/
bool _IsCompleteBinTree(pNode pRoot)
{
if (pRoot == NULL)
return false;
bool musthavenochild = false;
bool result = true;
queue<pNode> q;
q.push(pRoot);
while (!q.empty())
{
pNode Node = q.front();
q.pop();
if (musthavenochild)
{
if (Node->_pLeft != NULL&&Node->_pRight != NULL)
{
result = false;
break;
}
}
else
{
if (Node->_pLeft != NULL&&Node->_pRight != NULL)
{
q.push(Node->_pLeft);
q.push(Node->_pRight);
}
else if (Node->_pLeft != NULL&&Node->_pRight == NULL)
{
musthavenochild = true;
q.push(Node->_pLeft);
}
else if (Node->_pLeft == NULL && Node->_pRight != NULL)
{
result = false;
break;
}
else
{
musthavenochild = true;
}
}
}
return result;
}
/*14、求二叉树的镜像
如果二叉树为空直接返回空,
如果二叉树不为空,求左子树的镜像和右子树的镜像,然后将左右子树交换。*/
pNode _MirrorBinTree(pNode pRoot)
{
if (pRoot == NULL)
return NULL;
pNode pLeft = _MirrorBinTree(pRoot->_pLeft);
pNode pRight = _MirrorBinTree(pRoot->_pRight);
pRoot->_pLeft = pRight;
pRoot->_pRight = pLeft;
return pRoot;
}
/*15、将二叉搜索树转换成一个排序的双向链表。要求:不能
创建任何新的结点,只能调整树种结点指针的指向
如果二叉树为空,双向链表的第一个结点和最后一个结点都是NULL,
如果二叉树不为空:
如果左子树为空对应双向链表的第一个结点就是根结点,左边不需要任何操作
如果左子树不为空,转换左子树,二叉树对应双向链表的第一个结点就是左子树转换后双向链表的第一个结点,同时将根结点和左子树转换后的双向有序链表连接。
如果右子树为空,对应双向有序链表的最后一个结点是根结点,右边不需要任何操作,
如果右子树不为空,对应双向有序链表的最后一个结点就是右子树转换后双向有序链表的最后一个结点就是右子树转换后双向有序链表的最后一个结点,同时将根结点和右子树转换后的双向有序链表的第一个结点连接。*/
void _BinTree2List(pNode pRoot,pNode &pfirstNode, pNode &plastNode)
{
pNode pfirstLeft, plastLeft, pfirstRight, plastRight;
if (pRoot == NULL)
{
pfirstNode = NULL;
plastNode = NULL;
return;
}
if (pRoot->_pLeft == NULL)
{
pfirstNode = pRoot;
}
else
{
_BinTree2List(pRoot->_pLeft, pfirstLeft, plastLeft);
pfirstNode = pfirstLeft;
pRoot->_pLeft = plastLeft;
plastLeft->_pRight = pRoot;
}
if (pRoot->_pRight == NULL)
{
plastNode = pRoot;
}
else
{
_BinTree2List(pRoot->_pRight, pfirstRight, plastRight);
plastNode = plastRight;
pRoot->_pRight = pfirstRight;
pfirstRight->_pLeft = pRoot;
}
return;
}
private:
pNode _pRoot;
};
int main()
{
char arr[] = "ABD###CE##F";
BinTree<char> bt(arr, strlen(arr), '#');
bt.Preorder();
bt.Preordernonrecursive();
bt.Inorder();
bt.Inordernonrecursive();
bt.Postorder();
bt.Postordernonrecursive();
bt.Levelorder();
cout << "Height"<<bt.Hight() << endl;
cout <<"count"<< bt.count() << endl;
cout << "LeafCount"<<bt.LeafCount() << endl;
cout << "2层结点个数" << bt.KLevelCount(2) << endl;
cout << "3层结点个数" << bt.KLevelCount(3) << endl;
char c = 'E';
char b = 'D';
BinTreeNode<char>* Node = new BinTreeNode<char>(c);
BinTreeNode<char>* Node1 = new BinTreeNode<char>(b);
if (bt.IsInBinTree(Node))
cout << "this node is in BinTree" << endl;
else
cout << "this node is not in BinTree" << endl;
BinTreeNode<char> * ret = bt.CurrentAncestor(Node, Node1);
cout << ret->_data << endl;
cout << bt.IsAVLTree() << endl;
cout << bt.MaxLengthNode2Node() << endl;
char prearray[] = { 'A', 'B', 'D', 'C', 'E', 'F' };
char inarray[] = { 'D','B','A','E','C','F' };
BinTree<char> bt1;
BinTreeNode<char>* ret1 = bt1.ReconstructBinTree(prearray, inarray, 6);
cout << bt.IsCompleteBinTree() << endl;
BinTreeNode<char>* ret2 = bt.MirrorBinTree();
bt.BinTree2List();
return 0;
}