返回分类:全部文章 >> 基础知识
返回上级:编程基础 - 二叉树 (Binary Tree)
本文将介绍线索二叉树的基础知识,并用C++实现它。
在查看本文之前,需要一些数据结构和程序语言的基础,要对“树”和“二叉树 (Binary Tree)”的概念有一些了解。
其中的方法还需要熟悉“栈(stack)”、“队列(queue)”和“递归”。
线索二叉树:假设有 n 个结点的二叉树,其中存在 n + 1 个空指针,利用这些指针存放某种遍历下的前驱和后继,这样的指针叫做线索,这样形成的二叉树叫线索二叉树。
线索二叉树分为三种:
之后的代码,我们用中序线索二叉树来举例,另外两种原理相同,不再阐述。
在结构中,我们左子树与右子树需要一个标签来标注存放的是左右孩子还是前驱后继。
且对于每个结点的空指针域:
// 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;
}
};
初始化二叉树不再介绍,这里我们为每个结点加入线索。
由于我们需要保存前驱和后继,所以需要一个变量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());
}
由于我们多了线索,所以遍历可以更轻松了,只需要根据线索逐个进行即可。
以下以中序遍历为例。
// 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;
}
// 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; // 前驱
}
}
// 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);
}
}
测试的线索二叉树为:
// Author: https://blog.csdn.net/DarkRabbit
// Threaded Binary Tree
#include "threaded_binary_tree.h"
#include
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* 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;
}
BDAEC
CEADB
请按任意键继续. . .