文章目录
- 一 线索化二叉树简介
- 二 线索化规则
- 三 前序线索化
- 3.1 代码演示
 
- 四 中序线索化
- 4.1 代码演示
 
- 五 后序线索化
- 5.1 代码演示
 
一 线索化二叉树简介
- 线索化:将一颗二叉树的结点指向为空的指针,线索化为某一种顺序遍历的的指向下一个按顺序的结点的指针
- 一颗为顺序化的二叉树的前序遍历顺序
  
- 对其前序遍历线索化后,遍历顺序依次为:
  
二 线索化规则
- 结点的左指针,指向当前遍历顺序的直接前驱结点。
- 结点的右指针,指向当前遍历顺序的直接后继结点。
- 上图前序遍历线索化之后的指向如下图:
  
- 为了区分某个结点的指针指向是其左右孩子?还是某种顺序遍历线索【直接前驱、直接后继结点】?
- 需要在结点的结构体中增加一组标志位【左、右标志位】,来区分左右指针的指向代表的意义 
  - leftTag / rightTag为1,表示左 / 右指针指向的是前驱 / 后继结点
- leftTag / rightTag为0,表示左 / 右指针指向的是左孩子 / 右孩子
 
typedef char E;
typedef struct TreeNode {
    E element;
    struct TreeNode *left;
    struct TreeNode *right;
    //左、右标志位
    // 为1表示:这一边指向的是线索;为0表示:正常的孩子结点
    int leftTag, rightTag;
    //为后序遍历准备,指向双亲结点
    struct TreeNode *parent;
} *Node;
三 前序线索化
- 二叉树结构
  
  
- 具体的实现代码,希望诸君认真阅读体会!
3.1 代码演示
#include<iostream>
using namespace std;
typedef char E;
typedef struct TreeNode {
    E element;
    struct TreeNode *left;
    struct TreeNode *right;
    //左、右标志位
    // 为1表示:这一边指向的是线索;为0表示:正常的孩子结点
    int leftTag, rightTag;
    //为后序遍历准备,指向双亲结点
    struct TreeNode *parent;
} *Node;
Node pre = NULL;//保存前一个结点
Node createNode(E element) {
    Node node = (Node) malloc(sizeof(struct TreeNode));
    node->left = node->right = NULL;
    node->leftTag = node->rightTag = 0;
    node->element = element;
    return node;
}
//前序遍历线索化
void preOrderThreaded(Node root) {
    if (root == NULL) return;
    //---------线索化------------
    //判断当前结点的左边是否为空,如果是,就指向上一个结点
    if (root->left == NULL) {
        root->left = pre;
        //修改标志位
        root->leftTag = 1;
    }
    //判断上一个结点的右边是否为NULL
    // 如果为空,就进行线索化,指向当前结点
    if (pre && pre->right == NULL) {
        pre->right = root;
        pre->rightTag = 1;
    }
    pre = root;
    //----------------------
    //注意只有标志为0,才可以继续进行,否则,为线索
    if (root->leftTag == 0) {
        preOrderThreaded(root->left);
    }
    if (root->rightTag == 0) {
        preOrderThreaded(root->right);
    }
}
//前序遍历
void preOrder(Node root) {
    while (root) {
        cout << root->element << " ";
        if (root->leftTag == 0)
            root = root->left;
        else
            root = root->right;
        /*这里无所谓右边是孩子,还是线索*/
    }
}
int main() {
    Node a = createNode('A');
    Node b = createNode('B');
    Node c = createNode('C');
    Node d = createNode('D');
    Node e = createNode('E');
    a->left = b;
    b->left = d;
    a->right = c;
    b->right = e;
    preOrderThreaded(a);
    cout << "先序遍历的结果为:";
    preOrder(a);
    cout << endl;
    return 0;
}

四 中序线索化
- 二叉树中序线索化的结构
  
  
- 中序遍历线索化相较于前序遍历线索化,只具体的线索化代码的位置即可。
- 对二叉树进行中序便利的代码实现稍有复杂。
4.1 代码演示
- 具体的实现代码,希望诸君认真阅读体会!
#include<iostream>
using namespace std;
typedef char E;
typedef struct TreeNode {
    E element;
    struct TreeNode *left;
    struct TreeNode *right;
    //左、右标志位
    // 为1表示:这一边指向的是线索;为0表示:正常的孩子结点
    int leftTag, rightTag;
    //为后序遍历准备,指向双亲结点
    struct TreeNode *parent;
} *Node;
Node pre = NULL;//保存前一个结点
Node createNode(E element) {
    Node node = (Node) malloc(sizeof(struct TreeNode));
    node->left = node->right = NULL;
    node->leftTag = node->rightTag = 0;
    node->element = element;
    return node;
}
//中序遍历线索化
void inOrderThreaded(Node root) {
    if (root == NULL) return;
    //注意只有标志为0,才可以继续进行,否则,为线索
    if (root->leftTag == 0) {
        inOrderThreaded(root->left);
    }
    //---------线索化------------
    //判断当前结点的左边是否为空,如果是,就指向上一个结点
    if (root->left == NULL) {
        root->left = pre;
        //修改标志位
        root->leftTag = 1;
    }
    //判断上一个结点的右边是否为NULL
    // 如果为空,就进行线索化,指向当前结点
    if (pre && pre->right == NULL) {
        pre->right = root;
        pre->rightTag = 1;
    }
    pre = root;
    //-----------------------
    if (root->rightTag == 0) {
        inOrderThreaded(root->right);
    }
}
void inOrder(Node root) {
    while (root) {
        //先走到最左边结点
        //如果左边一直都不是线索,那就一直往左找,直到找到一个左边是线索为止
        while (root && root->leftTag == 0) {
            root = root->left;
        }
        //中序开始打印
        cout << root->element << " ";
        //左边打印完就该打印,右边 右边如果是线索化之后的结果表示 是下一个节点
        while (root && root->rightTag == 1) {
            root = root->right;
            cout << root->element << " ";
        }
        //最后继续向右节点开始 重复上述操作。
        root = root->right;
    }
}
int main() {
    Node a1 = createNode('A');
    Node b1 = createNode('B');
    Node c1 = createNode('C');
    Node d1 = createNode('D');
    Node e1 = createNode('E');
    a1->left = b1;
    b1->left = d1;
    a1->right = c1;
    b1->right = e1;
    pre = NULL;
    inOrderThreaded(a1);
    cout << "中序遍历的结果为:";
    inOrder(a1);
    cout << endl;
    return 0;
}

五 后序线索化
- 二叉树后序遍历线索化的结构:
  
  
- 后序遍历,需要先完成左右子树的遍历。左边子树遍历完成后,并不一定能找到右子树的根结点。比如:在遍历完左子数根节点B之后,就无法找到右树的根结点C。所以需要在节点结构体中设置设置指向双亲节点的指针。
5.1 代码演示
- 具体的实现代码,希望诸君认真阅读体会!
#include<iostream>
using namespace std;
typedef char E;
typedef struct TreeNode {
    E element;
    struct TreeNode *left;
    struct TreeNode *right;
    //左、右标志位
    // 为1表示:这一边指向的是线索;为0表示:正常的孩子结点
    int leftTag, rightTag;
    //为后序遍历准备,指向双亲结点
    struct TreeNode *parent;
} *Node;
Node pre = NULL;//保存前一个结点
Node createNode(E element) {
    Node node = (Node) malloc(sizeof(struct TreeNode));
    node->left = node->right = NULL;
    node->leftTag = node->rightTag = 0;
    node->element = element;
    return node;
}
//后序遍历线索化
void postOrderThreaded(Node root) {
    if (root == NULL) return;
    //注意只有标志为0,才可以继续进行,否则,为线索
    if (root->leftTag == 0) {
        postOrderThreaded(root->left);
        //左边完事之后,如果不为空,那么设定父子关系
        if (root->left != NULL)
            root->left->parent = root;
    }
    if (root->rightTag == 0) {
        postOrderThreaded(root->right);
        //右边完事之后,如果不为空,那么设定父子关系
        if (root->right != NULL)
            root->right->parent = root;
    }
    //---------线索化------------
    //判断当前结点的左边是否为空,如果是,就指向上一个结点
    if (root->left == NULL) {
        root->left = pre;
        //修改标志位
        root->leftTag = 1;
    }
    //判断上一个结点的右边是否为NULL
    // 如果为空,就进行线索化,指向当前结点
    if (pre && pre->right == NULL) {
        pre->right = root;
        pre->rightTag = 1;
    }
    pre = root;
    //---------------------------
}
void postOrder(Node root) {
    //记录上一次遍历的结点
    Node last = NULL, node = root;
    while (node) {
        //依旧是从整棵树最左边开始 同时加入防止无线循环的条件
        while (node->left != last && node->leftTag == 0) {
            node = node->left;
        }
        //左边遍历完了,如果右边是线索,就一路向前
        while (node && node->rightTag == 1) {
            cout << node->element << " ";
            last = node;
            node = node->right;
        }
        if (node == root && node->right == last) {
            //当左子树便利完成,我们需要去寻找左子树根节点的兄弟节点
            //通过parent拿到兄弟节点,但是如果当前结点是根节点,需要特殊处理,因为根节点没有父节点
            cout << node->element << " ";
            //后序遍历中,根节点一定是最后一个节点。所以直接返回就可以了。
            return;
        }
        //如果当前节点的上一个遍历的结点,那么就继续进行
        while (node && node->right == last) {
            cout << node->element << " ";
            last = node;
            node = node->parent;
        }
        //从左子树遍历上来,当前节点的右边,要么是线索,要么是右子树。
        if (node && node->rightTag == 0) {
            //如果不是线索,就直接走右边,如果是等到下一轮再说
            node = node->right;
        }
    }
}
int main() {
   	Node a2 = createNode('A');
    Node b2 = createNode('B');
    Node c2 = createNode('C');
    Node d2 = createNode('D');
    Node e2 = createNode('E');
    a2->left = b2;
    b2->left = d2;
    a2->right = c2;
    b2->right = e2;
    pre = NULL;
    postOrderThreaded(a2);
    cout << "后序遍历的结果为:";
    postOrder(a2);
    return 0;
}



















