图片和转换步骤来自这里
本文主要描述具体实现
用一种略微老土的话描述:
二叉树:每一节点最多有2个子节点,左边的叫左节点,右边的叫右节点,自己叫根节点。
树:每个节点的子节点数量不受限制。
森林:由若干个树构成的整体。
完了。
所以在你回忆完二叉树老生常谈的四种遍历后,又有那么一丁丁想要进军普通树的欲望的话,想想每个树节点应该怎么定义(毕竟想要转换成一个东西,好歹应该先弄清它里面是如何存数据的)。
树节点
每个树节点都有若干个类型相同的子节点,可以利用数组存储。所以我们的树节点有了它应该有的样子,像这样:
template<class T>
class TreeNode
{
public:
TreeNode(const T& element, size_t pSize = 0) :
m_pData(element),
m_pSize(pSize)
{
m_pNext = new TreeNode<T>*[m_pSize];
}
void setSize(size_t pSize)
{
m_pSize = pSize;
delete []m_pNext;
m_pNext = new TreeNode<T>*[m_pSize];
}
T m_pData;
size_t m_pSize;
TreeNode<T> **m_pNext;
};
因此,每次申请一个树节点指针时至少应该告诉它存储的数据,子节点大小可以在弄清楚有多少个子节点之后利用setSize()函数设置。
完成树节点的定义后,树的类也就没什么特别的,毕竟很少会遇见像是让你在树中插入删除一个节点这种变态的问题,那是(更特别的)二叉树做的事情。唯一需要的可能就一个输出函数(好歹检验一下转换的对不对噻?),其他个性化功能自己添加就好,重点放在二者转换上。
template<class T>
class Tree
{
public:
Tree(TreeNode<T> *pRoot = NULL); //构造函数
~Tree();//析构函数
void levelPrint() const; //层次输出
private:
TreeNode<T> *m_pRoot; //根节点
size_t m_pSize; //节点数量
};
简单提一句,二叉树节点包括两个节点指针(左右),其余的没什么区别。
二叉树->树
步骤1:
若某个节点X的左孩子存在,则将这个左孩子的右孩子节点,右孩子的右孩子节点,右孩子的右孩子的右孩子节点…,都作为节点X的孩子。将节点X与这些右孩子用线连接起来。
步骤2:
删除原二叉树中所有节点与其右孩子节点的连线(不包括根节点的右节点,右节点的右节点…….,因为树会劈叉啦~)。
步骤3:
层次调整。
首先应该明确的是,
1. 我们应该对二叉树的每一个节点都进行像步骤1那样的操作。
2. 对于步骤二,只需在构建树节点时不将原二叉树的某个根节点的右节点加到这个根节点的子节点数组(next数组)中即可。
3. 需要有个变量记录根节点,用于返回。
你可以试着带着这三步转换一棵二叉树(图片上的例子也不错),兴许在转换的过程中,你会发现比较好的方法来对每一个节点都做步骤1操作。
如果实在太懒,那就接着读吧(-__-)b
第一步,
从二叉树的根节点开始,目的是申请一个树节点,然后对它的next数组赋值。
所以无疑先计算它的子节点个数(查找个数在二叉树中进行),左节点算一个,然后不断查找右节点,直到为NULL。
(注:pNode是此时二叉树子树的根节点)
BinaryTreeNode<T> *pLeftNode = pNode->leftNode;
size_t pCnt = 0;
while(pLeftNode)
{
++pCnt;
pLeftNode = pLeftNode->rightNode;
}
第二步,
申请一个树节点指针作为这个位置子树的根节点,同时对next数组赋值(赋值的过程和计算子节点个数的过程类似,都是不断查找右节点,遇见一个二叉树的右节点就申请一个树节点):
(注:parentNode是此时树中子树的根节点)
TreeNode<T> *parentNode = new TreeNode<T>(pNode->m_pData, pCnt);
pLeftNode = pNode->