一、引用指针与二级指针
复习到中序遍历构建线索二叉树的时候,看到天勤的书上的代码和王道的不一样,在构建线索二叉树函数的代码的参数为:void Set_Thread(TBTNode *p,TBTNode *&pre),迟疑了下,TBTNode *&pre中的*&pre其实是对TBTNode *pre指针的引用,而我们知道指针的引用其实就是直接对指针本体进行操作,那是不是可以通过二级指针TBTNode **p_pre来等价*&pre相同效果呢?为什么*p可以传递指针而*pre要直接传递指针本体呢?其实和中序遍历构建线索二叉树的递归算法有关。
在中序遍历构建线索二叉树的递归算法中有
其实在算法执行过程中,两个指针的 作用不同,p是用于对节点进行操作的,在传参过程中传递节点的地址即TBTNode *p就可以了,而pre是用于记录当前节点p的前驱节点的,在整个递归过程中是唯一存在的,不是且不能随着一个函数的调用的返回而复原,如果不使用引用,那么在递归调用过程将随着当前递归调用的函数结束而返回上次指向的节点,pre将永远不能移动,我们为了让其在函数递归的过程中传递的永远都是这个指针而不是递归栈中函数复制参数创造的临时的pre所以我们要通过引用保证其唯一性。
是不是可以通过二级指针TBTNode **p_pre来等价*&pre相同效果呢?
可以
为什么*p可以传递指针而*pre要直接传递指针本体呢?
为了在递归调用过程中保证指针pre的唯一性,而用*p的目的是为记录结点地址,结点的唯一性不需要通过唯一的p来确定而是通过树的左右子来确定。
二、中序线索二叉树的建立
中序线索二叉树的建立是一个深度优先遍历的遍历顺序下的关键操作执行修改节点的深度优先遍历模式,用递归中序遍历的框架下,在其中关键操作来进行对节点指针域进行修改的一个解法,其中比较有趣的是我只使用两个指针就完成对前序节点和后续节点的连接:(1)指向当前节点的指针p(2)指向(当前节点的前驱结点)的指针pre,由此可见我们并没有设置指向当前节点(指针p指向的节点)的后继节点的指针,因此无法直接对当前结点(指针p指向的节点)操作来连接其后继结点;但我们换个思路,pre指向的节点相对于p指向的节点为前驱节点 ⇔ p指向的节点为pre指向的节点的后继结点,因此我们可以对pre指针进行操作来完成后继节点的连接
#include "stdlib.h"
#include "stdio.h"
typedef struct ThreadTNode
{
struct ThreadTNode *l_child,*r_child;
int data;
int l_tag,r_tag;
}TTNode,*p_TTNode;
void Set(p_TTNode &pre,p_TTNode &p)//递归过程中的关键操作
{
if(p->l_child == NULL)
{
p->l_child = pre;
p->l_tag = 1;
}
/*因为我们仅设置了两个指针----(1)指向当前节点的指针p(2)指向(当前节点的前驱结点)的指针pre,由此可见我们并没有设置指向当前节点(指针p指向的节点)的后继节点的指针,因此无法直接对当前结点(指针p指向的节点)操作来连接其后继结点;但我们换个思路,pre指向的节点相对于p指向的节点为前驱节点 ⇔ p指向的节点为pre指向的节点的后继结点,因此我们可以对pre指针进行操作来完成后继节点的连接*/
if(pre->r_child == NULL)
{
pre->r_child = p;
pre->r_tag = 1;
}
//将前驱指针更新为当前访问节点;p指针的更新会根据p指向的节点的位置的不同更新的值(p=p->l_c或r_c)而不同,因此在函数体内部不进行p的跟新,而是通过递归调用函数传参更新.
pre = p;//更新前驱节点
}
void CreateInThreadTree(p_TTNode &pre,p_TTNode &p)
{
//先序遍历递归结构:
if(p == NULL)//临界状态处理
{
return;
}
else
{
CreateInThreadTree(pre->l_child,p);
void Set(p_TTNode &pre,p_TTNode &p);
CreateInThreadTree(pre->r_child,p);
}
}
int main()
{
p_TTNode pre_p = nullptr;
p_TTNode root_p;
CreateInThreadTree(pre_p, root_p);
}