二叉树的创建我们已经了解了,现在我们在这一个基础上来拓展一些常出现的题
1、在二叉树中寻找一个节点——>存在,返回节点,不存在返回NULL
2、求一个节点的双亲节点——>存在,返回节点,不存在返回NULL
3、求二叉树的高度
4、求叶子节点的个数
5、求第K层的节点个数
6、求一个二叉树的镜像
……
不过在这这钱我们先来补充一下二叉树的性质:
1、若规定根节点的层次为0,则一个非空的二叉树的第i个节点最多有2^i(i>=0)个节点。
2、若规定只有根节点的深度为0,则深度为K的二叉树的最大节点数为2^(K+1)-1(K>=-1).
3、对任何二叉树,如果叶子节点为n0,度为n2的飞叶子节点的n2,则有n0=n2+1
证明过程如下:
4、具有n个节点的完全二叉树的深度K为log2(n+1)向上取整
懂得人都知道log后面的不是2*(n+1),而是“以2为底(n+1)”。具体怎么验证这个性质,那么不妨回顾一下完全二叉树的定义吧。而且这个性质的中心位置就是完全二叉树。
5、对具有N的节点的完全二叉树,如果按照从上到下,从左到右,的顺序对所有的节点进行排序,从0开始编号。则:i
①、i的双亲节点为(i-1)/2
②、i的左孩子,为2*i+1,右孩子为2*i+2
③、i=0,则为根节点,无双亲节点
④2*i+1>=N,2*i+2>=N,则没有双亲节点
了解了这些之后,我们再来看一下这些编程题:
1、在二叉树中寻找一个节点——>存在,返回节点,不存在返回NULL
如果用先序的遍历方式,那么用递归的话只需四步即可
非递归的话,也是可以参考先序遍历的非递归的写法,只需将对根节点的处理,改成和要查找的节点进行比较即可
Node* _Find(Node* pRoot, const T& value)
{
//递归:
//if (NULL == pRoot)//树为空,则返回空
// return NULL;
//if (pRoot->_value == value)//先确定根结点是不是所要查找的值
// return pRoot;
//_Find(pRoot->_pLeft, value);//再查找左右子树
//_Find(pRoot->_pRight, value);
//非递归:
if (pRoot == NULL)
return NULL;
stack<Node*> s;
s.push(pRoot);
while (!s.empty())
{
Node *pCur = s.top();
if (pCur->_value == value)//注意:两个value类型不一样
return pCur;
s.pop();//时刻记住,访问完后要出栈
if (pCur->_pRight)//注意,入栈顺序是先右后左
s.push(pCur->_pRight);
if (pCur->_pLeft)
s.push(pCur->_pLeft);
}
}
2、求一个节点的双亲节点——>存在,返回节点,不存在返回NULL
首先我们得将参数检验一下:对根结点检验,判断树是不是空树;对pCur检验,验证它是不是存在;还有一根特殊的情况,根结点没有双亲。之后其他的都是普通情况。再进行递归运算。
Node* _Parent(Node* pRoot, Node* pCur)
{
if (NULL == pRoot)//首先确定树是不是空的
return NULL;
//参数验证2:判断pCur是不是为NULL 或 根结点是不是pCur(根结点是没有双亲结点的)
if (NULL == pCur || pCur == pRoot)
return NULL;
if (pRoot->_pLeft)//在左子树中查找
return _Parent(pRoot->_pLeft, pCur);
return _Parent(pRoot->_pRight, pCur);//无论找没找到,都返回(这一步可以拆分开)
3、求二叉树的高度
一个二叉树的高度是多少?如果说根结点的层数为1的话,那最下面一层的层数就是二叉树的高度。那么用递归的话,它是左子树的高度和右子树高度的最大值再加1。
size_t _Height(Node* pRoot)
{
if (NULL == pRoot)//没有结点,返回0
return 0;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
return 1;
//遍历左右子树,返回左右子树最大值 +1(根结点)
return max(_Height(pRoot->_pLeft),_Height(pRoot->_pRight))+1;
}
4、求叶子节点的个数
首先想一下叶子结点的特点是啥,没有左右子树,所以用递归的话,这就是我们的一个递归出口,那么叶子结点的个数就是:递归左子树的个数加上递归右子树的个数。
size_t _GetLeafCount(Node* pRoot)
{
if (NULL == pRoot)
return 0;
//叶子结点
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
return 1;
//递归:左子树的叶子结点+右子树的叶子结点
return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);
}
5、求第K层的节点个数
第K层的结点个数,如果K=高度,那么这种情况和叶子结点个数还是不一样,叶子结点不一定都在同一层,所以,我们要求第K层的需要将左子树的第K-1层的计算出来,再加上右子树的第K-1层结点个数。
不过这里涉及参数检验的讨论,对于根结点的检验我们已经很熟悉了,判断其是不是空树就行了,不过这里还有一个参数:K
对于K来说,有一个特殊的情况那就是K==1,那么我们就返回1,至于对其进行参数检验,只要其不小于1就行了;那么你或许会问了,为什么不对它进行上限处理呢?需要吗?
/*假设我们进行了上限处理:K<Height(),我们这是递归,每一次进入这个函数走到这一步是不是都需要求一下高度,每次求高度还要将树遍历一下,那么算下来我们求了几次了,很多。而不加的时候会出错吗?我们假设K是5,(层数是3),那么到第四层(所有节点都是NULL)直接会从判断根结点为空的地方返回,所以,这是不会影响结果的。*/
//第k层的结点个数
size_t _GetKLevelCount(Node* pRoot, size_t k)
{
if (NULL == pRoot || k < 1)//注意:参数检验
return 0;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
return 1;
return _GetKLevelCount(pRoot->_pLeft, k - 1) + _GetKLevelCount(pRoot->_pRight, k - 1);
}
6、求一个二叉树的镜像
如图所示,我们看到,左边的是我们一直使用的一个二叉树,镜像之后我们可以直观上看到,两个二叉树的关系:关于中间的一条隐形的线(镜子)对称。那么接下来我们就来根据这一个性质来求一个二叉树的镜像
首先我们处理的是二叉树的特殊情况:
1、要是树为空,那么就不用处理了,直接返回就行了
2、镜像是将二叉树中的左子树和右子树交换,那么如果这个数只有一个节点,那么就不用交换了。所以这一个情况是,二叉树只有一个节点。
3.普通情况:这要是用递归的话,很简单的几句代码就搞定了:首先是将根节点的两个子树交换,之后再进入左子树进行递归,然后再进入右子树进行递归。
这样之后,就完了,不过非递归又该怎么写呢?
同样我们来屡一下思路:
1、首先还式对树进行判断:树空的话,返回NULL
2、其次创建一个数据结构(队列),将根节点入队列
3、取出对头元素,判断其左右子树,存在,则入队列
4、对头元素的左右子树。最后将对头元素出队列
void _BinaryMirror(Node* pRoot)
{
if (pRoot == NULL)
return;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)
return;
swap(pRoot->_pLeft, pRoot->_pRight);
if (pRoot->_pLeft!=NULL)
_BinaryMirror(pRoot->_pLeft);
if (pRoot->_pRight!=NULL)
_BinaryMirror(pRoot->_pRight);
}
void _BinaryMirror_Nor(Node* pRoot)
{
if (pRoot == NULL)
return;
queue<Node*> q;
q.push(pRoot);
while (!q.empty())
{
Node* pCur = q.front();//取队头元素
//如果左右孩子存在,将其保存在队列中
if (pCur->_pLeft)
q.push(pCur->_pLeft);
if (pCur->_pRight)
q.push(pCur->_pRight);
//交换左右
swap(pCur->_pLeft, pCur->_pRight);
//队头元素出队列
q.pop();
}
}
下面是完整的代码,以及测试的语句:
#include<iostream>
using namespace std;
#include<queue>
#include<stack>
// 孩子表示法
template<class T>
struct BinaryTreeNode
{
BinaryTreeNode(const T& value)
: _value(value)
, _pLeft(NULL)
, _pRight(NULL)
{}
T _value;
BinaryTreeNode<T>* _pLeft; // 左孩子
BinaryTreeNode<T>* _pRight; // 右孩子
};
template<class T>
class BinaryTree
{
typedef BinaryTreeNode<T> Node;//换个名字Node
public:
//构造函数①
BinaryTree()
:_pRoot(NULL)
{}
//构造函数②
BinaryTree(const T array[], size_t size, const T& invalid)
{
size_t index = 0;
_CreateBinaryTree(_pRoot, array, size, index, invalid);
}
//拷贝构造函数
BinaryTree(const BinaryTree<T>& bt)
{
_pRoot = _CopyBirnaryTree(bt._pRoot);
}
//赋值运算符重载
BinaryTree<T>& operator=(const BinaryTree<T>& bt)
{
if (this != &bt)
{
_CopyBirnaryTree(bt._pRoot);
}
return this;
}
//析构函数
~BinaryTree()
{
_DestroyBinaryTree(_pRoot);
}
// 层序遍历
void LevelOrder()
{
cout << "LevelOrder:" << endl;
_LevelOrder(_pRoot);
cout << endl;
}
//查找函数
Node* Find(const T& value)
{
return _Find(_pRoot, value);
}
// 找一个结点的双亲结点
Node* Parent(Node* pCur)
{
return _Parent(_pRoot, pCur);
}
//找一个结点的左孩子结点
Node* GetLeftChild(Node* pCur)
{
return (pCur == NULL) ? NULL : pCur->_pLeft;
}
//找一个结点的右孩子结点
Node* GetRightChild(Node* pCur)
{
return (pCur == NULL) ? NULL : pCur->_pRight;
}
//求二叉树的高度
size_t Height()
{
return _Height(_pRoot);
}
//求二叉树叶子结点的个数
size_t GetLeafCount()
{
return _GetLeafCount(_pRoot);
}
//求第K层结点的个数
size_t GetKLevelCount(size_t k)
{
return _GetKLevelCount(_pRoot, k);
}
//求二叉树的镜像 (非递归)
void BinaryMirror_Nor()
{
_BinaryMirror_Nor(_pRoot);
}
//求二叉树的镜像
void BinaryMirror()
{
_BinaryMirror(_pRoot);
}
private:
//创建二叉树
void _CreateBinaryTree(Node* &pRoot, const T array[], size_t size, size_t& index, const T& invalid)
{
if (index < size&&array[index] != invalid)
{
pRoot = new Node(invalid);//注意new的时候要和结构体中写的函数参数对应
pRoot->_value = array[index];
_CreateBinaryTree(pRoot->_pLeft, array, size, ++index, invalid);//注意:++index
_CreateBinaryTree(pRoot->_pRight, array, size, ++index, invalid);
}
}
// pRoot-->被拷贝树的根节点
Node* _CopyBirnaryTree(Node* pRoot)
{
if (pRoot == NULL)
return NULL;
Node *NewRoot = new Node(pRoot->_value);
NewRoot->_pLeft = _CopyBirnaryTree(pRoot->_pLeft);
NewRoot->_pRight = _CopyBirnaryTree(pRoot->_pRight);
return NewRoot;
}
//销毁二叉树
void _DestroyBinaryTree(Node*& pRoot)
{
Node* temp = pRoot;
if (temp == NULL)
return;
_DestroyBinaryTree(temp->_pLeft);
_DestroyBinaryTree(temp->_pRight);
delete temp;
temp = NULL;
}
Node* _Find(Node* pRoot, const T& value)
{
//递归:
//if (NULL == pRoot)//树为空,则返回空
// return NULL;
//if (pRoot->_value == value)//先确定根结点是不是所要查找的值
// return pRoot;
//_Find(pRoot->_pLeft, value);//再查找左右子树
//_Find(pRoot->_pRight, value);
//非递归:
if (pRoot == NULL)
return NULL;
stack<Node*> s;
s.push(pRoot);
while (!s.empty())
{
Node *pCur = s.top();
if (pCur->_value == value)//注意:两个value类型不一样
return pCur;
s.pop();//时刻记住,访问完后要出栈
if (pCur->_pRight)//注意,入栈顺序是先右后左
s.push(pCur->_pRight);
if (pCur->_pLeft)
s.push(pCur->_pLeft);
}
}
//查找pCur的双亲结点
Node* _Parent(Node* pRoot, Node* pCur)
{
if (NULL == pRoot)//首先确定树是不是空的
return NULL;
//参数验证2:判断pCur是不是为NULL 或 根结点是不是pCur(根结点是没有双亲结点的)
if (NULL == pCur || pCur == pRoot)
return NULL;
if (pRoot->_pLeft)//在左子树中查找
return _Parent(pRoot->_pLeft, pCur);
return _Parent(pRoot->_pRight, pCur);//无论找没找到,都返回(这一步可以拆分开)
}
size_t _Height(Node* pRoot)
{
if (NULL == pRoot)//没有结点,返回0
return 0;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
return 1;
//遍历左右子树,返回左右子树最大值 +1(根结点)
return max(_Height(pRoot->_pLeft),_Height(pRoot->_pRight))+1;
}
size_t _GetLeafCount(Node* pRoot)
{
if (NULL == pRoot)
return 0;
//叶子结点
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
return 1;
//递归:左子树的叶子结点+右子树的叶子结点
return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);
}
//第k层的结点个数
size_t _GetKLevelCount(Node* pRoot, size_t k)
{
if (NULL == pRoot || k < 1)//注意:参数检验
return 0;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
return 1;
return _GetKLevelCount(pRoot->_pLeft, k - 1) + _GetKLevelCount(pRoot->_pRight, k - 1);
}
void _BinaryMirror(Node* pRoot)
{
if (pRoot == NULL)
return;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)
return;
swap(pRoot->_pLeft, pRoot->_pRight);
if (pRoot->_pLeft!=NULL)
_BinaryMirror(pRoot->_pLeft);
if (pRoot->_pRight!=NULL)
_BinaryMirror(pRoot->_pRight);
}
void _BinaryMirror_Nor(Node* pRoot)
{
if (pRoot == NULL)
return;
queue<Node*> q;
q.push(pRoot);
while (!q.empty())
{
Node* pCur = q.front();//取队头元素
//如果左右孩子存在,将其保存在队列中
if (pCur->_pLeft)
q.push(pCur->_pLeft);
if (pCur->_pRight)
q.push(pCur->_pRight);
//交换左右
swap(pCur->_pLeft, pCur->_pRight);
//队头元素出队列
q.pop();
}
}
private:
Node* _pRoot; // 指向树的根节点
};
void FunTest2()
{
char* pStr = "124###35##6";
BinaryTree<char> bt(pStr, strlen(pStr), '#');
//cout << bt.Find('5')->_value << endl;//注意:传参的类型
cout << bt.Parent(bt.Find('5'))->_value << endl;
//cout << bt.Height() << endl;//高度
//cout << bt.GetLeafCount() << endl;//叶子结点个数
//cout << bt.GetKLevelCount(3) << endl;
//bt.BinaryMirror(); //????
//bt.BinaryMirror_Nor();
}
int main()
{
FunTest2();
return 0;
}