看了网上不少线索二叉树的遍历流程代码:拿着严版教材一通copy就直接发到网上,关键的一些代码全都不说明。导致我自己琢磨好久
现在就把我琢磨的东西发出来分享!
那我来讲一下我学习的过程中看到这遇到的问题:
1.如何寻找当前节点的后继
2.如何确保中序线索遍历一直进行下去
下面我拿一个例子来进行讲解
我就拿我这个图来说,我先把他变成线索二叉树形态:
在编写代码的时候,首先就会思考第一个问题:如何寻找当前节点的下一个后继节点!比如说我要寻找9的后继,比如我要找7的后继节点,再比如我要找1的后继节点
首先解决第一个小问题:
寻找9的后继节点很简单:因为变成了线索树,只需要按照紫色箭头所指向的方向走下去即可:
反应到代码可以这么写:这个地方用到p->rtag就很简单了,只要p这个节点存在直接后继,我就让p走下去
if(p->rtag==1)
q=p->right;
第二个小问题:
如何找到7的后继:我们可以看到图中写到的中序遍历顺序是7到10,那么如何寻找呢?这里就需要总结出一个规律,当一个节点不能靠线索走下去的时候,那么他的后继就是其右孩子的最左边的子树,反应到图中就是7的孙子节点10,也就是9的左子树。
这里如果我们再变一下,假设10还存在n个左子树,那么7这个节点的后继就是10的最左边的那个孙子节点,反应到代码是这样的
q=p->right; while(q->ltag==0) q=q->left;
代码的意思就是先将当前节点转化为其右节点,然后再沿其右节点一直往左子树的方向走
但是!注意我这里为什么没有直接写q=q->left;而是写为while条件语句!原因就和第三个小问题有关:
我们在寻找1这个节点的后继时发现,其右子树为3,当沿着3的左子树走下去的时候,发现3不存在左子树;3
就是1的直接后继。那么该怎么办呢?
可不可以将代码改成
while(q->left!=NULL) q=q->left;
答案是不行。
看上面我画的线索图,3有一条前驱指针指向1本身,如果按照上面的写法将会形成一个死循环:1指向3,3指向1,无法出来。
所以我们这个地方一定要将while条件判断改成指向当前节点的“左孩子”,左孩子的判断很简单:当前节点的ltag=0的时候一定就有左孩子
你可能会问:
若q的左孩子是空怎么办?当前不会为空,之前线索化二叉树的时候已经将所有的空指针线索化了,tag==0就说明指针指向了孩子,tag==1就代表指针指向了前驱或者后继结点,不存在指针为空的情况。
那么我们的求节点后继的函数就可以这么写
Node* next(Node *p) { Node *q=NULL; if(p->rtag==1) q=p->right; else { q=p->right; while(q->ltag==0) q=q->left; } return q; }
写完了如何找到线索二叉树的后继,那么接下来就是如何遍历线索二叉树了:
我们值到中序二叉树的起点都是最左边的节点开始遍历,那么如何定位到最左边的节点呢?只需要用一个while循环:从根节点开始,不断遍历其左孩子,一直到rtag==1时,就说明不存在左孩子了,进而找到了我们需要的最左边的节点。
while(p->ltag==0) { p=p->left; } printf("%d ",p->data);
这个地方printf出最左边节点的data,原因是最左边的孩子是中序遍历的第一个节点,自然需要打印出来
然后就只需要把线索二叉树当成一个链表,不断的往左走就可以了
while(p->right!=NULL) { p=next(p); printf("%d ",p->data); }
所以整个的代码可以写成这样
void RunInthread(Node *node) { if(node==NULL) return; Node *p=node; if(p!=NULL) { while(p->ltag==0) { p=p->left; } printf("%d ",p->data); while(p->right!=NULL) { p=next(p); printf("%d ",p->data); } } }