线索二叉树

原文:https://subetter.com/algorith...

一:背景

  线索二叉树的定义为:一个二叉树通过如下的方法“穿起来”:所有应该为空的右孩子指针指向该节点在中序序列中的后继,所有应该为空的左孩子指针指向该节点的中序序列的前驱。
  那么在有N个节点的二叉树中共有N+1个空指针,这些空指针就叫做“线索”。(补充:在N个节点的二叉树中,每个节点有2个指针,所以一共有2N个指针,将这N个节点连接起来需要N-1条线,即使用了N-1个指针。所以剩下2N-(N-1)=N+1个空指针。)
  那线索二叉树有何妙用呢?由于巧妙地利用了空指针,所以它可以快速地查找到二叉树中某节点的前驱和后继。接下来具体介绍这个数据结构。
  在进行下文之前,约定如下:

struct Node
{
    bool left_thread;
    bool right_thread;
    char data;
    Node * left;
    Node * right;

    Node()
    {
        left_thread = right_thread = false;
        data = 0;
        left = right = nullptr;
    }
};

class  ThreadedBinaryTree
{
public:
    ThreadedBinaryTree();
    Node * create();
    void threaded(Node * cur, Node *& pre);  //线索化
    void formLoop();                         //构环
    Node * get_successor(Node * node);       //返回后继节点
    Node * get_precursor(Node * node);       //返回前驱节点
    void in_order_print();                   //中序遍历
private:
    Node * _header;  //头结点
    Node * _root;    //二叉树根节点
};

  请注意,在决定left是指向左孩子还是前驱,right是指向右孩子还是后继,我们是需要一个区分标志的。因此我们在Node结构体中增设两个布尔变量left_thread和right_thread,其中:
  (1)left_thread为true时指向前驱,为false时指向该节点的左子树;
  (2)right_thread为true时指向后继,为false时指向该节点的右子树。

二:具体实现与代码分析

2.1 构造二叉树

ThreadedBinaryTree::ThreadedBinaryTree()
{
    _root = create();
}

Node * ThreadedBinaryTree::create()
{
    Node * p = nullptr;
    char ch;
    cin >> ch;
    if (ch == '.')  //结束输入
        p = nullptr;
    else
    {
        p = new Node;
        p->data = ch;
        p->left = create();
        p->right = create();
    }
    return p;
}

  递归构造一棵二叉树,这在二叉树基础已经讲过,不作解释。

2.2 线索化及二叉树构环

void ThreadedBinaryTree::threaded(Node * cur, Node *& pre)
{
    if (cur == nullptr)
        return;
    else
    {
        //按照中序遍历方向,先处理左子树
        threaded(cur->left, pre);

        //再处理当前节点
        if (cur->left == nullptr)
        {
            cur->left_thread = true;
            cur->left = pre;
        }
        if (cur->right == nullptr)
            cur->right_thread = true;
        if (pre->right_thread)
            pre->right = cur;
        pre = cur;

        //最后处理右子树
        threaded(cur->right, pre);
    }
}

void ThreadedBinaryTree::formLoop()
{
    _header = new Node;  //创建头结点,并完成初始化
    _header->left_thread = true;
    _header->right_thread = true;
    _header->left = _header->right = _header;

    Node * pre = _header;  //记录中序遍历的前一个节点
    threaded(_root, pre);  //进行线索化

    pre->right_thread = true;  //线索化完后,把中序遍历的最后一个节点即pre,指向header
    pre->right = _header;
    _header->left = pre;  //注意,header的左指针指向中序遍历的最后一个
}

  _header节点的作用就是把线索化后的二叉树串起来,形成一个环。_header的左孩子指向中序遍历序列的最后一个节点,右孩子指向中序遍历序列的第一个节点,如下图:

2.3 前驱和后继

Node * ThreadedBinaryTree::get_successor(Node * node)
{
    if (node->right_thread)
        return node->right;
    Node * p = node->right;
    while (p->left_thread == false)  //已线索化,故此处只能用left_thread来判断左子树的情况
        p = p->left;
    return p;
}

Node * ThreadedBinaryTree::get_precursor(Node * node)
{
    if (node->left_thread)
        return node->left;
    Node * p = node->left;
    while (p->right_thread == false)  //已线索化,故此处只能用right_thread来判断右子树的情况
        p = p->right;
    return p;
}

2.4 中序遍历

void ThreadedBinaryTree::in_order_print()
{
    cout << "中序遍历为:";
    Node * p = _header->right;  //header的右节点指向二叉树中序遍历的第一个节点
    while (p != _header)
    {
        cout << p->data << " ";
        p = get_successor(p);
    }
    cout << endl;
}

三:完整代码

/**
*
* author 刘毅(Limer)
* date   2017-03-26
* mode   C++
*/
#include
using namespace std;

struct Node
{
    bool left_thread;
    bool right_thread;
    char data;
    Node * left;
    Node * right;

    Node()
    {
        left_thread = right_thread = false;
        data = 0;
        left = right = nullptr;
    }
};

class  ThreadedBinaryTree
{
public:
    ThreadedBinaryTree();
    Node * create();
    void threaded(Node * cur, Node *& pre);  //线索化
    void formLoop();                         //构环
    Node * get_successor(Node * node);       //返回后继节点
    Node * get_precursor(Node * node);       //返回前驱节点
    void in_order_print();                   //中序遍历
private:
    Node * _header;  //头结点
    Node * _root;    //二叉树根节点
};

int main()
{

    ThreadedBinaryTree my_tree;
    my_tree.formLoop();
    my_tree.in_order_print();

    return 0;
}

ThreadedBinaryTree::ThreadedBinaryTree()
{
    _root = create();
}

Node * ThreadedBinaryTree::create()
{
    Node * p = nullptr;
    char ch;
    cin >> ch;
    if (ch == '.')  //结束输入
        p = nullptr;
    else
    {
        p = new Node;
        p->data = ch;
        p->left = create();
        p->right = create();
    }
    return p;
}

void ThreadedBinaryTree::threaded(Node * cur, Node *& pre)
{
    if (cur == nullptr)
        return;
    else
    {
        //按照中序遍历方向,先处理左子树
        threaded(cur->left, pre);

        //再处理当前节点
        if (cur->left == nullptr)
        {
            cur->left_thread = true;
            cur->left = pre;
        }
        if (cur->right == nullptr)
            cur->right_thread = true;
        if (pre->right_thread)
            pre->right = cur;
        pre = cur;

        //最后处理右子树
        threaded(cur->right, pre);
    }
}

void ThreadedBinaryTree::formLoop()
{
    _header = new Node;  //创建头结点,并完成初始化
    _header->left_thread = true;
    _header->right_thread = true;
    _header->left = _header->right = _header;

    Node * pre = _header;  //记录中序遍历的前一个节点
    threaded(_root, pre);  //进行线索化

    pre->right_thread = true;  //线索化完后,把中序遍历的最后一个节点即pre,指向header
    pre->right = _header;
    _header->left = pre;  //注意,header的左指针指向中序遍历的最后一个
}

Node * ThreadedBinaryTree::get_successor(Node * node)
{
    if (node->right_thread)
        return node->right;
    Node * p = node->right;
    while (p->left_thread == false)  //已线索化,故此处只能用left_thread来判断左子树的情况
        p = p->left;
    return p;
}

Node * ThreadedBinaryTree::get_precursor(Node * node)
{
    if (node->left_thread)
        return node->left;
    Node * p = node->left;
    while (p->right_thread == false)  //已线索化,故此处只能用right_thread来判断右子树的情况
        p = p->right;
    return p;
}

void ThreadedBinaryTree::in_order_print()
{
    cout << "中序遍历为:";
    Node * p = _header->right;  //header的右节点指向二叉树中序遍历的第一个节点
    while (p != _header)
    {
        cout << p->data << " ";
        p = get_successor(p);
    }
    cout << endl;
}

  以(2.2)中的图为例,输入数据及测试结果为:

你可能感兴趣的:(c++,数据结构,二叉树,遍历)