所谓中序化,就是把闲着空余的指针指向遍历的前驱和后继,举个例子:
如图所示,DEF节点没有左孩子和右孩子,(对于C的右孩子指针同理)浪费了指针,所以要让DEF的左右孩子指针指向DEF的前驱和后继,(左孩子指向前驱,右孩子指向后继)这个前驱和后继是相对于顺序表而言的,这个顺序就是中序遍历的顺序,即图中的 D B E A F C 。
那么指针指向的是怎样的呢,我画图表示一下:
因为D没有前驱,所以指向空,D的后继是B,所以 D 的右孩子指向 B ,E,F 同理,左孩子指向各自的前驱,右孩子指向各自的后继,对于 C,因为只有右孩子为空,所以只有右孩子指向后继,也就是空。
接下来我们可以思考如何实现部分节点的指针的重构呢?
既然是中序遍历,我们可以回忆一下普通的中序遍历是怎么进行的:
void inOrder(struct TreeNode* T) {
if (T == NULL) {
;
}
else {
inOrder(T->lchild);
printf("%d ", T->val);
inOrder(T->rchild);
}
}
如上面代码所示,T 就是每次中序遍历的节点,也就是说 T 按照中序遍历的顺序不断更新。
在这个函数中,实现的是 T 指向节点值的打印,那么也就是说我们可以更换printf("%d ", T->val);
让函数在中序遍历时实现其他功能,即(改变节点左右孩子的功能)。
类似于这样:
void inOrder(struct TreeNode* T) {
if (T == NULL) {
;
}
else {
inOrder(T->lchild);
if(T-lchild == NULL){
T->lchild = T 的前驱
}
if(T-rchild == NULL){
T->rchild = T 的后继
}
inOrder(T->rchild);
}
}
那么接下来就有一个问题,如何找到前驱和后继,还有,如何体现节点的线索化,既然 T 是按照中序遍历来进行的,我们可以让一个指针 pre 和 T 相等,但是每次比 T 前一个节点,实现方法就是,每次更新 pre = T 之后,T接着进行遍历。其原理与链表中:pre = cur,cur = cur->next 类似,在二叉树中,因为 指针pre 需要更新值,所以*pre = T;,传入函数的是TreeNode** pre二级指针。
刚才提到要体现节点的线索化,原因就是为了中序遍历线索二叉树,我们要给结构体加入左右tag,标记指向的是孩子还是前驱后继,代码如下:
typedef struct TreeNode {
char val;
struct TreeNode* lchild;
struct TreeNode* rchild;
int ltag;
int rtag;
}TreeNode;
void inOrderThreadTree(TreeNode* T,TreeNode** pre) {
if (T == NULL) {
;
}
else {
inOrderThreadTree(T->lchild,pre);
//printf("%c ", T->val);
if (T->lchild == NULL) {
T->ltag = 1;
T->lchild = *pre;
}if (*pre != NULL &&(*pre)->rchild == NULL) {
(*pre)->rtag = 1;
(*pre)->rchild = T;
}
*pre = T;//更新*pre的值,T继续向前遍历一个节点
inOrderThreadTree(T->rchild,pre);
}
}
这里第一个 if (T->lchild == NULL)很容易理解,如果左孩子为空指向前驱 *pre,第二个 if 意思是*pre的右孩子为空的时指向 T 即 *pre 的后继,也就是说,*pre 和 T 这样进行遍历的:
我画了几个例子帮助理解,黑色箭头代表 if 成立指向的新构建的线索。还有当遍历到最终节点时,这时要让 pre->rtag = 1; pre->rchild = NULL;因为最后一个节点因为没有后继节点没有进入递归,所以要把右孩子置为空。
接着要中序遍历线索二叉树,首先要找到第一个节点:
TreeNode* GetFirst(TreeNode* T) {
while (T->lchild&& T->ltag!=1)
{
T = T->lchild;
}
return T;
}
接着要找下一个节点,如果右孩子rtag为1,那么也就是说右孩子就是后继,直接返回右孩子,其实就是根,如果右孩子rtag为0的话,也就是说有右孩子,需要继续找到新子二叉树的最左节点,getfirst(node->rchild),所以最后就是 左->根->右 的顺序,代码如下:
TreeNode* GetNext(TreeNode* node) {
if (node->rtag == 1) {
node = node->rchild;
}
else {
node = GetFirst(node->rchild);
}
return node;
}
最后是主函数,要循环中序遍历:
int main() {
char array[15] = "ABD##E##CF###";
int index = 0;
TreeNode* pre = NULL;
TreeNode* T;
MyTreeCreat(&T, array, &index);
inOrderThreadTree(T,&pre);
pre->rtag = 1;
pre->rchild = NULL;
for (TreeNode* node = GetFirst(T); node != NULL; node = GetNext(node)) {
printf("%c ", node->val);
}
return 0;
}
主要是for循环这一步,先找到第一个节点,然后一直在GetNext()函数中循环,直到最后为跳出。结果如下:
这就是文章的全部内容了,希望对你有所帮助,如有错误欢迎指出。