补上很久前在线写后浏览器崩溃一篇文章,重写了一次
线索树: 二叉树中利用NULL链域的一种变形二叉树中度数为0或1的节点有2个或1个的链域,其值为NULL,这些链域都浪费了,所以对这些链域加以利用,使得二叉树的某些操作效率更佳.
ps: 如果以计算一棵二叉树的所有链域 (n0 n1 n2 三种节点,度数分别为0,1,2) 度数: 子节点的个数, 则树的总链域数 为 2*(n2+n1+n0),而值为NULL的链域数为n1+2*n0;(n0=n2+1) ,所以如果有2n 个链域,就有 n+1 个链域是无用的,这是一种浪费
线索的含义:1)如果节点有左子树,左链域就指向左子树,否则,就指向其在树 中序遍历中 的前驱节点
2)如果节点有右子树,右链域就指向右子树,否则,就指向其在树 中序遍历中 的后驱节点
ps:由于链域有两种用途,所以要另外设置两个标志域,指明链域的用途下面是节点的结构:
template<class T>
class tnode
{
public:
tnode(T data=T(),
bool ltd=false, tnode* lcd=NULL,
bool rtd=false, tnode* rcd=NULL)
:data(data),ltd(ltd),lcd(lcd),rtd(rtd),rcd(rcd) { }
~tnode(){}
/* override new delet trace the memroy */
void* operator new(size_t size)
{
static int num=0;
void *p = malloc(size);
printf("%d malloc-> %p\n", ++num,p);
return p;
}
void operator delete(void* p)
{
static int num=0;
printf("%d free-> %p\n", ++num,p);
free(p);
}
private:
bool ltd;
bool rtd;
T data;
tnode* lcd;
tnode* rcd;
friend class ttree<T>;
};
template<class T>
class ttree
{
public:
typedef tnode<T>* tree;
ttree();
~ttree();
..... // 省略函数
private:
tree root;
};
几个重要的操作(ps:下面的操作都是在中序线索树的特定操作)
a.寻找树中节点中序遍历中的后续节点
思路:
1,获取该节点右子树的最左下节点(存在右子树)通过不断判断是否存在左子树一直往左走即可
2,直接获取后续节点(不存在右子树)
代码:
/* 寻找线索树节点中序遍历中的后续节点 */
template<class T>
tnode<T>*& ttree<T>::insucc(tree& ptrNode)
{
tree temp=ptrNode->rcd;
tree pre=NULL;
if(!ptrNode->rtd)
while(!temp->ltd){
pre=temp;
temp=temp->lcd;
}
return pre==NULL?ptrNode->rcd:pre->lcd;
}
b.寻找树中节点中序遍历中的前驱节点
思路:
和a一样. 1中改成获取左子树的最右下节点; 2一样
代码:
template<class T>
tnode<T>*& ttree<T>::inprecc(tree& ptrNode)
{
tree temp=ptrNode->lcd;
tree pre=NULL;
if(!ptrNode->ltd)
while(!temp->rtd){
pre=temp;
temp=temp->rcd;
}
return pre==NULL?ptrNode->lcd:pre->rcd;
}
c.insert操作(1,右插入 2,左插入)ps:插入:插入的节点替代位置节点的右/左子树根节点
以下的操作都是存在右子树或左子树的情况下操作的
1,右插入
思路:
I, 把位置节点Node的右子树插入到待插入节点INode的右链域中
II, INode插入后的左子树是空的,所以左线索(前驱节点)指向Node
III,修改INode的新右子树的左下方的节点的左线索(前驱节点)指向INode,此节点就是INode的(中序遍历)后继节点
代码:
template<class T>
void ttree<T>::insert_right(tree& ptrNode,const T&data)
{
tree nnd=new tnode<T>;
nnd->data=data;
nnd->rtd=ptrNode->rtd; nnd->rcd=ptrNode->rcd;
nnd->ltd=true; nnd->lcd=ptrNode;
ptrNode->rcd=nnd;
ptrNode->rtd=false;
if(!nnd->rtd){
tree& p_succ=insucc(nnd);
p_succ->lcd=nnd;
}
}
2,左插入
思路:
I,
把Node的左子树插入到待插入节点INode的左链域
II, INode的右线索指向Node
III, 修改INode的新左子树右下方的节点的右线索(后继节点)指向INode,INode的(中序遍历)的前驱节点
代码:
template<class T>
void ttree<T>::insert_left(tree& ptrNode, const T&data)
{
tree nnd=new tnode<T>;
nnd->data=data;
nnd->ltd=ptrNode->ltd; nnd->lcd=ptrNode->lcd;
nnd->rtd=true; nnd->rcd=ptrNode;
ptrNode->lcd=nnd;
ptrNode->ltd=false;
if(!nnd->ltd){
tree& p_precc=inprecc(nnd);
p_precc->lcd=nnd;
}
}
d. 寻找节点Node的直接父节点fNode,利用线索化的二叉树很容易做到这个操作.
思路:
Node可能是fNode的左子树的根节点,或是fNode的右子树的根节点
1.左子树,只要不断寻找Node右子树的最右下的节点,该节点的后续节点(右线索)就是fNode
2.右子树,与左子树差不多,Node左子树最左下节点的前驱节点(左线索)
代码:
template <class T>
tnode<T>* ttree<T>::fPnode(tree& node)
{
/* find parent of node */
if(node==root->lcd) return node;
tree temp=node;
tree bak=temp;
/* case 1:left child */
while(!temp->rtd) temp=temp->rcd;
if(temp->rcd->lcd==node)
return temp->rcd;
/* case 2:right child */
temp=bak;
while(!temp->ltd) temp=temp->lcd;
if(temp->lcd->rcd==node)
return temp->lcd;
}