一、名称定义
do something:执行某项任务
renew:值的更新
preOrder:前序遍历
change-type:变形操作,将二叉树Tree类型转变为List类型
root:根节点,即:在main中最开始的那个二叉树节点(node)
二、理解
这次学习的是前序二叉树及其遍历。这个和中序遍历有很多相似的地方,相同的地方就不在赘述,详情见数据结构——树:二叉树的线索化及中序构建遍历
首先,还是要构建树的数据结构并且搭建二叉树
#include"iostream"
#include"cstdlib"
#include"cstring"
#define ElemType char
using namespace std;
typedef struct TreeNode
{
ElemType data;
struct TreeNode* lchild;
struct TreeNode* rchild;
int ltag;
int rtag;
}TreeNode;
void createTree(TreeNode** T, ElemType* data, int* index)
{
char ch;
ch = data[*index];
*index += 1;
if (ch == '#')
{
*T = nullptr;
}
else
{
*T = (TreeNode*)malloc(sizeof(TreeNode));
(*T)->data = ch;
(*T)->ltag = 0;
(*T)->rtag = 0;
//we define the "false" means the node is not visited
createTree(&((*T)->lchild), data, index);
createTree(&((*T)->rchild), data, index);
}
}
int main(void)
{
TreeNode* T;
int index = 0;
TreeNode* pre = nullptr;
char data[] = "ABD##E##CF##G##";
createTree(&T, data, &index);
return 0;
}
对于前序遍历,其基本的逻辑和中序遍历差不多,但是有些许的不同,关键就是在于转变函数preThreadTree()。
对于如上的一个二叉树,进行前序遍历(root-left-right),所得的List为:A-B-D-E-C。
现在对它执行change-type操作。
三、代码实现
1.1 preThreadTree(TreeNode* T, TreeNode** pre_node):用于将Tree类型的二叉树转化为List类型的二叉树。
void preThreadTree(TreeNode* T, TreeNode** pre_node)
{
if(T != nullptr)
{
if(T->lchild == nullptr)
{
T->ltag = 1;
T->lchild = *pre_node;
}
if (*pre_node != nullptr && (*pre_node)->rchild == nullptr)
{
(*pre_node)->rtag = 1;
(*pre_node)->rchild = T;
}
*pre_node = T;
if (T->ltag == 0) // have left-child
{
//if T->lchild != nullptr, execute this function, or execute the "if-program"
preThreadTree(T->lchild, pre_node);
}
//*pre_node = T;
//after you have finished manipulating the left-child, then you should deal with the
//right-child
preThreadTree(T->rchild, pre_node);
}
}
int main(void)
{
TreeNode* T;
int index = 0;
TreeNode* pre = nullptr;
char data[] = "ABD##E##CF##G##";
createTree(&T, data, &index);
preThreadTree(T, &pre);
pre->rtag = 1;
pre->rchild = nullptr;
return 0;
}
1.2 解析
前序遍历和中序遍历之间的关系就和三种递归遍历之间的关系是相同的。就是“做事”和“更新”两者之间的位置不同而已。中序遍历的搭建逻辑是:
inThreadTree(T->lchild, pre_node);
//do something
inThreadTree(T->rchild, pre_node);
前序遍历的逻辑是:
//do something
inThreadTree(T->lchild, pre_node);
inThreadTree(T->rchild, pre_node);
但是在这里需要注意的是,由于do something在renew前面,所以可能会造成某个node为空,导致段错误,因此在renew的时候需要进行判断。
由前文所提到的二叉树模型进行分析。在main函数中传入的为root。进入就执行 if 判断
由于 A != nullptr 且 A->lchild == B != nullptr,不执行
由于 在main中将pre_node 初始化为了nullptr,故不执行第二个 if
更新pre_node为T,开始进行递归,同时不要忘记判断:
1.当T的左指针空间为0时,说明其指向了下一个节点,走preThreadTree(T->lchild, pre_node)直到找到最左边的node这里的逻辑和inOrder一样
2.if判断后,再执行preThreadTree(T->rchild, pre_node)
四、完整框架代码
#include"iostream"
#include"cstdlib"
#include"cstring"
/*
This is oppose to the inOrder binary-tree which is
called "previous clue-binary tree(先序线索二叉树)". The basic logic is the same as the inOrder
The basic logic of it is that, if the node which is pointed has no left-child and right-child
then, this pointer point to the the previous node and behind node. Or, point to the
corresponding child.
*/
using namespace std;
typedef struct TreeNode
{
char data;//used to save the data
struct TreeNode* lchild; // child pointers
struct TreeNode* rchild;
int ltag;//used to record the condition of the left and right node
int rtag;
}TreeNode;
//create the binary-tree
//the basic logic is similar to the previous achievement
//but you shuold initate the ltag and rtag
void createTree(TreeNode** T, char* data, int* index)
{
char ch;
ch = data[*index];
*index += 1;
if (ch == '#')
{
*T = nullptr;
}
else
{
*T = (TreeNode*)malloc(sizeof(TreeNode));
(*T)->data = ch;
(*T)->ltag = 0;
(*T)->rtag = 0;
//we define the "false" means the node is not visited
createTree(&((*T)->lchild), data, index);
createTree(&((*T)->rchild), data, index);
}
}
/*
the inOrder traverse Clue-Binary tree
change the binary-tree into the clue-binary tree
*/
void preThreadTree(TreeNode* T, TreeNode** pre_node)
{
/* Before you write the tree, you should change your mind first or in order words, you
shoud tranform the perspective you see the binary-tree. Every time you manipulate the binary-tree,
you should see the "left-child", "middle-child" and "right-child" as one binary-tree because you
will use recursion to continue manipulating the next node. So, the next step is delivering the
left-child or the right-child to the function.
*/
//
//
//There, "T" is the root of the binary tree
//traverse the tree from the left to middle and then the right.
//because the root node doesn't have previous node, so the pre_node is "nullptr"
//FRONT-NODE
if (T != nullptr)
{
if (T->lchild == nullptr)
{
//is the left-child is nullptr, assign the ltag is "1"
T->ltag = 1;
//and then let the left-child point to the previous node
T->lchild = *pre_node;
}
//BEHIND-NODE
//after you finish manipulating the lchild, you should deal with the previous node.
if (*pre_node != nullptr && (*pre_node)->rchild == nullptr)
{
(*pre_node)->rtag = 1;
(*pre_node)->rchild = T;
}
*pre_node = T;
if (T->ltag == 0) // have left-child
{
//if T->lchild != nullptr, execute this function, or execute the "if-program"
preThreadTree(T->lchild, pre_node);
}
//*pre_node = T;
//after you have finished manipulating the left-child, then you should deal with the
//right-child
preThreadTree(T->rchild, pre_node);
}
}
//traverse the clue-binary tree
//because the binary-tree is linear construction now, you can find the head of the list
TreeNode* getNext(TreeNode* node)
{
if (node->rtag == 1||node->ltag == 1)
{
return node->rchild;
}
else
{
return node->lchild;
}
}
int main(void)
{
TreeNode* T;
int index = 0;
TreeNode* pre = nullptr;
char data[] = "ABD##E##CF##G##";
createTree(&T, data, &index);
preThreadTree(T, &pre);
pre->rtag = 1;
pre->rchild = nullptr;
for (TreeNode* node = T; node != nullptr; node = getNext(node))
{
cout << node->data;
}
cout << endl;
return 0;
}