线索树(上)

线索树是一种利用二叉树中NULL链域的变形,旨在提高特定操作的效率。通过在度为0或1的节点链域中存储中序遍历的前后驱节点信息,实现非递归的中序遍历。本文介绍了线索树节点结构及中序遍历中寻找后续节点和前驱节点的操作方法,以及左插入的步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

补上很久前在线写后浏览器崩溃一篇文章,重写了一次大哭

线索树: 二叉树中利用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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值