线索二叉树C++实现

什么是线索二叉树

线索二叉树主要体现在二叉树的 DFS 中, 也就是前序, 中序, 与后序遍历, 我们以中序遍历为例, 讲述线索二叉树线索的原理.

线索主要表现在, DFS 的过程中记录遍历的前驱与后继节点, 可以用下面的图来表示节点,

lbit
lbit
lchild
lchild
data
data
rchild
rchild
rbit
rbit

代码表示就是

struct Tree{
    Tree *left, *right;
    int number;
    int lbit, rbit;  // 用于记录节点的左右指针是前驱还是后继
};

首先, 我们先来回顾一下二叉树的中序遍历, (本文讲述的以下算法均是基于中序遍历的),

二叉树的中序遍历:

用代码表示就是:

void Midorder(Tree *node)
{
    if(node != nullptr)
    {
        Midorder(node->left);
        VISIT(node);    // 访问节点
        Midorder(node->right);
    }
}

对于下图的中序遍历, 我们遍历的顺序就是 D, B, J, E, A, H, F, I, C, G.

1
1
A
A
1
1
B
B
1
1
0
0
D
D
0
0
1
1
E
E
0
0
0
0
J
J
0
0
1
1
C
C
1
1
1
1
F
F
1
1
0
0
H
H
0
0
0
0
I
I
0
0
0
0
G
G
0
0
Head
Head

接下来, 我们以此图来构造出一个线索二叉树, 构造线索二叉树只有两个原则:

prior 是中序遍历序列中的当前节点的前一个节点, 比如 A的prior 就是 E 节点,

  1. 若当前访问的结点的左指针域为空,则它指向prior指的结点, 同时置该节点 lbit 的值为 0;
  2. 若prior所指结点的右指针域为空,则它指向当前访问的结点, 同时置前驱节点的 rbit 为 0。

因为我们每次都要记录 prior 节点, 因此我们不妨将 prior 设置为全局变量, 这样访问起来较为方便

而构建这个索引的代码, 我们可以在中序遍历的基础上完成:

Tree *prior = *head;

void Mid_order(Tree *node)
{
    if(node != nullptr)
    {
        Mid_order(node->left);
        if(node->left == nullptr)
        {
            node->lbit = 0;
            node->left = prior;
        }
        if(prior->right == nullptr)
        {
            prior->rbit = 0;
            prior->right = node;
        }
        VISIT(node);
        prior = node;
        Mid_order(node->right);
    }
}

假设我们最先访问的是head节点, 及第一个prior 节点是 head 节点. 现在我们构造出了下面这样的线索二叉树,

1
1
1
A
A
1
1
1
1
B
B
1
1
0
0
D
D
0
0
1
1
E
E
0
0
0
0
J
J
0
0
1
1
C
C
1
1
1
1
F
F
1
1
0
0
H
H
0
0
0
0
I
I
0
0
0
0
G
G
0
0
1
1
Head
Head
1
1

从线索二叉树找前驱与后继

那么对于已经构造好的线索二叉树, 我们怎样才能找到二叉树中某个节点的前驱与后继呢? 也就是说在不遍历的情况下, 直接找到前驱节点与后继节点, 我们从构造线索的两个规律入手,

找前驱:

  1. 根据上述的规律一, 我们很容易想到, 若当前节点的 node->lbit == 0, 那么当前节点的 left 所指的节点, 就是前驱节点,
  2. 若当前节点的 node->lbit == 1, 那么以node 的左节点为根节点的子树中,沿着该子树的右子树方向查找,直到某结点的 rbit == 0, 该节点就是节点 node 的前驱节点, 这个很容通过中序遍历的原理得到, 因为 node 节点的左子树必然比 node 先遍历, 也就是其前驱, 而一棵树的最后被遍历的节点就是最右下方的那个节点,

找后继: 找后继的方法与找前驱的方法类似, 都是通过上述规律以及中序遍历的规律得到的,

  1. 根据上述的规律二, 我们很容易想到, 若当前节点的 node->rbit == 0, 那么当前节点的 right 所指的节点, 就是后继节点,
  2. 若当前节点的 node->rbit == 1, 那么沿着 node 的右子树的根的左子树方向查找,直到某结点的 rbit == 0, 该节点就是节点 node 的前驱节点, 这个很容通过中序遍历的原理得到, 因为 node 节点的右子树必然比 node 后遍历, 也就是其后继, 而一棵树的最先被遍历的节点就是最左下方的那个节点,

线索二叉树相当于在树的结构上构造了一个双向链表, 本文采用的是中序遍历的方式讲解, 也主要是因为在排序二叉树中, 中序遍历获得的就是从小到大或从大到小的排序, 也就是有顺序的排序, 如果能构造出方便寻找的前驱与后继的方法, 那么排序与查找大小数字就很方便了.

以寻找后继为例, 我们可以通过下面代码完成:

Tree* Next_Node(Tree *Now_node)
{
    Tree * temp;
    temp = Now_node->right;
    if(Now_node->rbit == 1)
    {
        while(Now_node->lbit == 1)
            temp =  temp->left;
    }
    return  temp;
}
posted @ 2020-02-24 18:44  虾野百鹤  阅读(520)  评论(0编辑  收藏  举报