对于一个普通的二叉树,知道前序,中序,后序任意一种遍历序列都无法唯一确定一颗二叉树。因为如果知道前序或后序,只是知道那个是根结点,但是无法区分左右孩子,而知道了中序遍历,不知道根结点。通常只有知道一棵树的“前序、中序”或者“中序、后序”才能唯一确定这棵树。
而将一颗普通的二叉树转换为对应的拓展二叉树后根据得到的前序遍历或后序遍历就能唯一确定一颗二叉树。
扩展二叉树是把一棵树的左右孩子都补全,用#号表示,这样的话每一个结点,除了补全的#结点,其他结点一定都有两个孩子。
对于拓展二叉树
前序遍历 就相当于普通遍历的前序+中序 后序遍历 就相当于普通遍历的 后续+中序
拓展二叉树前序遍历
对这颗拓展二叉树进行前序遍历得到:ABD###C#E##
接下来就要开始讨论为什么根据这个先序遍历,就可以唯一确定一个二叉树
-
前序遍历A一定是根结点,又因为是拓展二叉树,那么A一定有左孩子B靠近A,那么B是A的左孩子。
-
因为前序遍历是先根在左在右,那么靠近B的D就是B的左孩子,D后面跟着两个#,说明D是原来二叉树的叶子结点。那么B结点的左子树就遍历完了,接着就是B的右孩子(#),说明原来的二叉树的结点B没有右孩子
-
现在知道了结点BD###构成了拓展二叉树的左子树,那么结点C#E##构成了拓展二叉树的右子树。根据前序遍历 根结点 —> 左子树 —> 右子树的规律,C结点就是根节点的右孩子,C结点后的#是C结点的左孩子,那么原二叉树C结点没有左孩子,E是右孩子,且是原二叉树的叶子结点。
拓展二叉树后序遍历
对这颗拓展二叉树进行后序遍历得到:##D#B###ECA
接下来就要开始讨论为什么根据这个先序遍历,就可以唯一确定一个二叉树
-
根据后序遍历可以知道A一定是根结点,又因为是拓展二叉树,那么C结点一定是A的右孩子,同理E是C的右孩子,根据之前前序遍历的分析知道E节点前面有两个##说明E是原二叉树的叶子节点,那么B后面的#是C结点的左孩子,原二叉树中C节点无左孩子。
-
现在知道了结点###EC构成了根结点的右子树,那么结点##D#B构成根结点的左子树。根据后序遍历知B结点是左子树的根结点,即B是A的左孩子。又因为是拓展二叉树,B左边的#是B的右孩子,那么原二叉树B结点无右孩子。D是B的左孩子,是原二叉树的叶子节点。
现在理论将完了,接下来就是代码实现了。
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* Left; // 指向当前节点左孩子
struct BinaryTreeNode* Right; // 指向当前节点右孩子
char data; // 当前节点数据域
}BinaryTreeNode,* BinaryTree;
二叉树建立(代码)
void CreateBinaryTree(BinaryTree* Tree)
{
char ch;
scanf("%c", &ch);
if (ch == '#')
{
*tree=NULL;
return;
}
else
{
*Tree = malloc(sizeof(BinaryTreeNode));
(*Tree)->data = ch;
CreateBinaryTree(&((*Tree)->Left));
CreateBinaryTree(&((*Tree)->Right));
}
}
对于之前讨论的那颗二叉树,只要输入ABD###C#E##或ABD###C#E##就可以构建成了。
二叉树的遍历
前序遍历
void PreOrderTraversal(BinaryTree Tree)
{
if (!Tree)
return;
else
{
printf("%c", Tree->data);
PreOrderTraversal(Tree->Left);
PreOrderTraversal(Tree->Right);
}
}
中序遍历
void InOrderTraversal(BinaryTree Tree)
{
if (!Tree)
return;
else
{
InOrderTraversal(Tree->Left);
printf("%c", Tree->data);
InOrderTraversal(Tree->Right);
}
}
后序遍历
void PostOrderTraversal(BinaryTree Tree)
{
if (!Tree)
return;
else
{
PostOrderTraversal(Tree->Left);
PostOrderTraversal(Tree->Right);
printf("%c", Tree->data);
}
}
对举例的二叉树进行验证
层次遍历
具体实现思路如下
①将根节点root 加入队列q
②取出队首结点,访问它
③若该结点有左孩子,则将左孩子入队
④该结点有有孩子,则将右孩子入队
重复进行步骤②,直到队列为空
//为了方便这里就用C++写了
void LayerOrder(BinaryTree* Tree)
{
queue<BinaryTreeNode*> q; //记得导入头文件<quene>
q.push(*Tree);
while (!q.empty())
{
BinaryTreeNode* now = q.front();
cout << now->data << endl;
q.pop();
if (now->Left != NULL)
q.push(now->Left);
if (now->Right != NULL)
q.push(now->Right);
}
}