编程基础 - 线索二叉树 (Threaded Binary Tree)

返回分类:全部文章 >> 基础知识

返回上级:编程基础 - 二叉树 (Binary Tree)

本文将介绍线索二叉树的基础知识,并用C++实现它。

在查看本文之前,需要一些数据结构和程序语言的基础,要对“树”和“二叉树 (Binary Tree)”的概念有一些了解。

其中的方法还需要熟悉“栈(stack)”、“队列(queue)”和“递归”。


文章目录

  • 编程基础 - 线索二叉树 (Threaded Binary Tree)
    • 1 线索二叉树简述 (Introduction)
    • 2 线索二叉树的结构 (Structure)
    • 3 为结点加入线索 (Add Thread)
    • 4 线索二叉树的遍历 (Traversal)
      • 4.1 获取首尾结点 (First and Last Node)
      • 4.2 获取后继前驱结点 (Successor and Predecessor Node)
      • 4.3 中序遍历 (Inorder Traversal)
    • 5 主函数与测试 (Main Method and Testing)
      • 5.1 主函数 (Main Method)
      • 5.2 打印结果 (Print Output)

1 线索二叉树简述 (Introduction)

线索二叉树:假设有 n 个结点的二叉树,其中存在 n + 1 个空指针,利用这些指针存放某种遍历下的前驱和后继,这样的指针叫做线索,这样形成的二叉树叫线索二叉树。

线索二叉树分为三种:

  • 中序线索二叉树
  • 前序线索二叉树
  • 后续线索二叉树

之后的代码,我们用中序线索二叉树来举例,另外两种原理相同,不再阐述。


2 线索二叉树的结构 (Structure)

在结构中,我们左子树与右子树需要一个标签来标注存放的是左右孩子还是前驱后继。

且对于每个结点的空指针域:

  • 左子树指针空:存放前驱指针
  • 右子树指针空:存放后继指针
// Author: https://blog.csdn.net/DarkRabbit
// Threaded Binary Tree// 线索标签,标记指针存放的是左右孩子还是前驱后继
enum ThreadTag
{LeftOrRightChild = 0, // 左孩子或右孩子PredecessorOrSuccessor = 1 // 前驱或后继
};// 线索二叉树节点
template<typename T>
class ThreadNode
{public:T element; // 数据ThreadTag leftTag; // 左标签ThreadTag rightTag; // 右标签ThreadNode<T>* leftChild; // 左指针ThreadNode<T>* rightChild; // 右指针ThreadNode(const T& e){element = e;leftTag = ThreadTag::LeftOrRightChild;rightTag = ThreadTag::LeftOrRightChild;leftChild = 0;rightChild = 0;}~ThreadNode(){leftChild = 0;rightChild = 0;}
};

3 为结点加入线索 (Add Thread)

初始化二叉树不再介绍,这里我们为每个结点加入线索。

由于我们需要保存前驱和后继,所以需要一个变量predNode来存储上一个结点。

而加入线索,我们需要对所有节点遍历。即,只是将二叉树的中序遍历中将访问节点(Visit)换成了加入线索。

对于每个结点的空指针域:

  • 左子树指针空:存放前驱指针
  • 右子树指针空:存放后继指针

C++代码:

  • 递归方式:

    // Author: https://blog.csdn.net/DarkRabbit
    // Threaded Binary Tree
    // 递归初始化中序遍历线索
    template<typename T>
    void InitializeInorderThread(ThreadNode<T>* node, ThreadNode<T>*& predNode)
    {if (node != 0){// 左标识if (node->leftChild == 0){node->leftTag = ThreadTag::PredecessorOrSuccessor;}else{node->leftTag = ThreadTag::LeftOrRightChild;}// 右标识if (node->rightChild == 0){node->rightTag = ThreadTag::PredecessorOrSuccessor;}else{node->rightTag = ThreadTag::LeftOrRightChild;}InitializeInorderThread(node->leftChild, predNode); // 左子树(L)// 左子树为空,则NULL或前驱if (node->leftTag == ThreadTag::PredecessorOrSuccessor){node->leftChild = predNode;}// 如果前驱存在,且右子树为空,则NULL或后继if (predNode != 0 && predNode->rightTag == ThreadTag::PredecessorOrSuccessor){predNode->rightChild = node;}predNode = node;InitializeInorderThread(node->rightChild, predNode); // 右子树(R)}
    }
    
  • 非递归方式:

    // Author: https://blog.csdn.net/DarkRabbit
    // Threaded Binary Tree
    // 初始化中序遍历线索
    template<typename T>
    void InitializeInorderThread(ThreadNode<T>* root)
    {if (root == 0){return;}std::stack<ThreadNode<T>*> nodeStack;ThreadNode<T>* predNode = 0;ThreadNode<T>* loopNode = root;do{while (loopNode != 0) // 左子树(L){if (loopNode->leftChild == 0){loopNode->leftTag = ThreadTag::PredecessorOrSuccessor;}else{loopNode->leftTag = ThreadTag::LeftOrRightChild;}if (loopNode->rightChild == 0){loopNode->rightTag = ThreadTag::PredecessorOrSuccessor;}else{loopNode->rightTag = ThreadTag::LeftOrRightChild;}nodeStack.push(loopNode);loopNode = loopNode->leftChild;}if (!nodeStack.empty()){loopNode = nodeStack.top();nodeStack.pop();// 左子树为空,则NULL或前驱if (loopNode->leftTag == ThreadTag::PredecessorOrSuccessor){loopNode->leftChild = predNode;}// 如果前驱存在,且右子树为空,则NULL或后继if (predNode != 0 && predNode->rightTag == ThreadTag::PredecessorOrSuccessor){predNode->rightChild = loopNode;}predNode = loopNode;loopNode = loopNode->rightChild; // 右子树(R)}} while (loopNode != 0 || !nodeStack.empty());
    }
    

4 线索二叉树的遍历 (Traversal)

由于我们多了线索,所以遍历可以更轻松了,只需要根据线索逐个进行即可。

以下以中序遍历为例。

4.1 获取首尾结点 (First and Last Node)

// Author: https://blog.csdn.net/DarkRabbit
// Threaded Binary Tree
// 中序遍历第一个结点
template<typename T>
ThreadNode<T>* ThreadTreeInorderFirstNode(ThreadNode<T>* root)
{if (root == 0){return 0;}ThreadNode<T>* first = root;while (first->leftTag == ThreadTag::LeftOrRightChild){first = first->leftChild;}return first;
}
// Author: https://blog.csdn.net/DarkRabbit
// Threaded Binary Tree
// 中序遍历最后一个结点
template<typename T>
ThreadNode<T>* ThreadTreeInorderLastNode(ThreadNode<T>* root)
{if (root == 0){return 0;}ThreadNode<T>* last = root;while (last->rightTag == ThreadTag::LeftOrRightChild){last = last->rightChild;}return last;
}

4.2 获取后继前驱结点 (Successor and Predecessor Node)

// Author: https://blog.csdn.net/DarkRabbit
// Threaded Binary Tree
// 中序遍历后继结点
template<typename T>
ThreadNode<T>* ThreadTreeInorderSuccessor(ThreadNode<T>* node)
{if (node == 0){return 0;}if (node->rightTag == ThreadTag::LeftOrRightChild) // 如果有右子树{return ThreadTreeInorderFirstNode(node->rightChild); // 返回右子树中序第一个结点}else // 如果没有右子树,找线索{return node->rightChild; // 后继}
}
// Author: https://blog.csdn.net/DarkRabbit
// Threaded Binary Tree
// 中序遍历前驱结点
template<typename T>
ThreadNode<T>* ThreadTreeInorderPredecessor(ThreadNode<T>* node)
{if (node == 0){return 0;}if (node->leftTag == ThreadTag::LeftOrRightChild) // 如果有左子树{// 返回左子树中序最后一个结点(最后一个没有右子树的结点)return ThreadTreeInorderLastNode(node->leftChild);}else // 如果没有左子树,找线索{return node->leftChild; // 前驱}
}

4.3 中序遍历 (Inorder Traversal)

// Author: https://blog.csdn.net/DarkRabbit
// Threaded Binary Tree// 中序遍历
template<typename T>
void ThreadTreeInorderTraversal(ThreadNode<T>* root, void(*pVisit)(ThreadNode<T>&))
{for (ThreadNode<T>* node = ThreadTreeInorderFirstNode(root);node != 0; node = ThreadTreeInorderSuccessor(node)){(*pVisit)(*node);}
}// 中序反向遍历
template<typename T>
void ThreadTreeInorderTraversalReverse(ThreadNode<T>* root, void(*pVisit)(ThreadNode<T>&))
{for (ThreadNode<T>* node = ThreadTreeInorderLastNode(root);node != 0;node = ThreadTreeInorderPredecessor(node)){(*pVisit)(*node);}
}

5 主函数与测试 (Main Method and Testing)

测试的线索二叉树为:

A
B
C
NULL
D
E
NULL

5.1 主函数 (Main Method)

// Author: https://blog.csdn.net/DarkRabbit
// Threaded Binary Tree#include "threaded_binary_tree.h"
#include <iostream>
using namespace std;
using namespace BinaryTrees;typedef ThreadNode<int>* ThreadedBinaryTree;// 打印访问结点
void PrintVisitedElement(ThreadNode<int>& node)
{cout << (char)(node.element);
}int main()
{ThreadedBinaryTree tree = new ThreadNode<int>('A');tree->leftChild = new ThreadNode<int>('B');tree->rightChild = new ThreadNode<int>('C');tree->leftChild->rightChild = new ThreadNode<int>('D');tree->rightChild->leftChild = new ThreadNode<int>('E');InitializeInorderThread(tree); // 非递归//ThreadNode<int>* pred = 0;//InitializeInorderThread(tree, pred); // 递归ThreadTreeInorderTraversal(tree, PrintVisitedElement);cout << endl;ThreadTreeInorderTraversalReverse(tree, PrintVisitedElement);cout << endl;delete tree->rightChild->leftChild;delete tree->leftChild->rightChild;delete tree->rightChild;delete tree->leftChild;delete tree;//system("pause"); VC++return 0;
}

5.2 打印结果 (Print Output)

BDAEC
CEADB请按任意键继续. . .

编程基础 - 线索二叉树 (Threaded Binary Tree)相关推荐

  1. C语言实现线索二叉树Threaded Binary Tree (附完整源码)

    C语言实现线索二叉树Threaded Binary Tree 树节点定义 实现以下7个接口 完整实现和main测试源码 树节点定义 typedef struct Node {int data; /** ...

  2. 【霍罗维兹数据结构】线索二叉树 | THREADED BINARY TREES

    前言 最近在读霍罗维兹的<数据结构基础>(Fundamentals of Data Structures in C),本篇博客为阅读笔记和知识总结. 0x00 线索(threads) 具有 ...

  3. js实现数据结构及算法之二叉树(Binary Tree)

    树(Tree) 树是一种非线性的数据结构,分层存储 树常被用来存储具有层级关系的数据,也会被用来存储有序列表等 树和集合一样,不允许相同的元素存在 树由一组以边连接的节点组成 一棵树最上面的节点称为根 ...

  4. 二叉树(Binary Tree):先序遍历、中序遍历、后序遍历和层次遍历

    二叉树(Binary Tree):先序遍历.中序遍历.后序遍历和层次遍历 树 Tree 二叉树 Binary Tree 先序遍历 Preorder Traversal 中序遍历 Inoreder Tr ...

  5. 数据结构学习笔记(六):二叉树(Binary Tree)

    目录 1 背景知识:树(Tree) 2 何为二叉树(Binray Tree) 2.1 二叉树的概念与结构 2.2 满二叉树与完全二叉树 2.3 二叉树的三种遍历方式 3 二叉树及其遍历的简单实现(Ja ...

  6. 二叉树(Binary Tree)详解

    二叉树本身就是递归定义的(wikipedia): To actually define a binary tree in general, we must allow for the possibil ...

  7. Threaded Binary Tree

    "一个二叉树通过如下的方法"穿起来":所有原本为空的右(孩子)指针改为指向该节点在中序序列中的后继,所有原本为空的左(孩子)指针改为指向该节点的中序序列的前驱." ...

  8. 数组反向遍历ios_LeetCode106.从中序与后序遍历序列构造二叉树(Construct Binary Tree from Inor...)...

    106. 从中序与后序遍历序列构造二叉树 根据一棵树的中序遍历与后序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 中序遍历 inorder = [9,3,15,20,7]后序遍 ...

  9. 二叉树(Binary Tree,BT)的概念和性质

    二叉树是一种比较特殊的树形结构,也比较常见,其特点是每个节点最多只有二棵子树,即二叉树中不存在度大于2的节点.二叉树的子树有左子树.右子树之分,孩子同样也有左孩子.右孩子之分(次序不能颠倒). 一般地 ...

最新文章

  1. python 图像压缩后前端解压_Python在后台自动解压各种压缩文件的实现方法
  2. 复旦副教授魏忠钰:AI和人类对垒「奇葩说」,如何打造智能辩手?
  3. php函数dirname范例,PHP dirname( )用法及代碼示例
  4. sql server 2008学习12 事务和锁
  5. @property的必要性
  6. 在JDT中使用Java 8 Lambda
  7. leetcode 188. 买卖股票的最佳时机 IV(dp)
  8. webflux上传下载文件
  9. JavaScript 中的string 方法
  10. 手把手系列|风控建模中共线性的影响和处理(上)
  11. NLP预训练家族 | Text-To-Text范式统一NLP任务
  12. Leetcode 105. 前序和中序遍历序列构造二叉树
  13. 股价/期货等时间序列数据的整合检验、Grach建模
  14. 记录三个困扰很久的问题
  15. jquery 调用ajax返回json
  16. php导出指定格式excel,php导出excel格式文件的例子
  17. 计算机不用时怎样休眠,怎么样设置电脑长时间不用进入休眠
  18. 计算机组成原理笔记 半导体随机存储器RAM和ROM
  19. zencart和php是什么,什么是ZenCart ZenCart有什么特点 ZenCart模板
  20. 码牛学院安卓Android移动互联网高级开发正式课学习笔记

热门文章

  1. 2021北京大学暑期课程:区块链与隐私计算
  2. mysql使用结巴语句_结巴分词 java 高性能实现,是 huaban jieba 速度的 2倍
  3. 王道机组笔记IEEE754
  4. 嗨格式数据恢复的 10 种最佳替代方法
  5. 应用层HTTP数据包的截获与还原技术的实现
  6. 【光通信光互联电信模块详解】
  7. 安卓APP开发发展趋势与前景
  8. Requests: 1, Fetched: 0, Skipped: 0, Processed: 0
  9. 原创 METTLER TOLEDO托利多Bplus 条码格式设置教程(scale manager)
  10. linux(debian 11)下安装nginx