二叉树的叶节点的两个指针都没有利用,一些分支节点也可能有一个指针指向NULL,这些指针造成了程序在内存空间上的浪费,但这些叶节点也必不可省。所以我们可以把这些指向NULL的指针,重新赋值,左指针则指向其所在遍历序列里的前驱节点,右指针则指向其所在遍历序列里的后继节点。然后在同序列遍历的时候,我们可以直接使用节点的指针成员存储的遍历结果,从而加快遍历速度。采用这种方案,对于中序排列,对于程序性能有最大的改善。因为所有叶节点可以在中序遍历序列里最为均匀的分布,和分支节点间隔分布,因为中序序列是“左根右”。而且,对于线索化的二叉树,我们还可以实现中序遍历的逆序遍历,即“右根左”,扩展了遍历方向,类似双向链表的双向访问。
以中序遍历为例,如果某一节点的右子节点为空,则其右子节点指向其遍历序列里的后继节点。如果其右子节点非空,则要找到以其右子节点为根的右子树的最左非空叶节点。因为我们要遵循中序“左根右”的遍历规则。
线索化后,稍加改动判别条件,仍然可以对二叉树递归遍历,不受线索化的影响,本文做了线索化后中序递归的验证。先序后序层序则还未进行验证。
以下图为例,可见,中序遍历,带空指针的叶节点在遍历序列里分布的最为均匀。对于右子节点非空的分支节点,求其后继节点,在逻辑上也变的非常简单,只需要找到其右子树的最左下端叶节点即可。对于线索化后的“右根左”的中序逆序遍历,对于左指针非线索,则找到其左子树的最右叶节点即可。逻辑上比递归要简单了些。
黑色字符所示为叶节点。
中序遍历线索化,是伴随着对二叉树的中序遍历进行的,所以要对二叉树中序遍历一次。以递归遍历为合适,因为可以突出线索化的主要任务,忽略复杂又细致的递归过程。获得二叉树节点的排序同时,进行线索化。每遍历到一个节点,就要对该节点的指针进行考察。若是叶节点和不到俩子节点的分支节点,对其空指针指向进行修正。左指针指向其前驱节点,右指针指向其后继节点。遍历结束,线索化也就完成了。具体操作就是:用一个静态局部或者全局指针变量始终保存当前节点的前驱节点的地址,为这对排序中相邻的俩节点建立指针上的关联。中序线索化二叉树时,只有遍历序列的最后一个叶节点的右指针为空,且指针标记仍为指针,而非线索。序列里第一个节点的左指针也为空,但其左指针类别为线索。
以下是程序里的函数功能:
由逗号表达式建立二叉树,函数createBiTree。
根据二叉树输出其逗号表达式形式,以检验二叉树的建立是否正确,函数displayBiTree。
由非递归方法中序遍历二叉树并输出到屏幕,函数inOrderNoRecursion,属于复习之前的知识点,也是给后面的带线索二叉树的中序遍历做对比,检验函数运行结果。
建立线索化二叉树,threadBiTree。
依靠线索中序遍历二叉树并输出,inOrderThreadBiTree。
由递归方法中序遍历线索二叉树,inOrderThreadBiTreeRecurse(也是可以的,但线索二叉树里很少有空指针了,所以以前依靠节点为空null的判别条件,应改为其指针是否为线索,若为线索,等价于指针原来指向null),
按中序的逆序(右根左)遍历输出线索二叉树,函数rightRootLeftOrderThreadedBiTree。
删除线索二叉树,函数deleteThread,源于bilibili懒猫老师的作业。课程网址(我就是跟她学习的):懒猫老师-数据结构-(31)线索二叉树1(线索链表,二叉树线索化)_哔哩哔哩_bilibili
https://www.bilibili.com/video/BV1kk4y1R777
还有bilibili小甲鱼老师:(小甲鱼)数据结构与算法(全99讲完结版)_哔哩哔哩_bilibili
https://www.bilibili.com/video/BV1Ws411T7Qk?p=49&t=676
对于没有线索化的二叉树,递归删除思路是:删除其左右子节点,再删除根节点,遇到空节点则返回,属于后序遍历。对于线索化后的二叉树,很少有空节点了,所以不能以待删除节点为空节点为递归依据了,但可以根据左右子节点的标志结合子节点是否为空来判别左右子节点的性质,仍然可以用递归的方法。那么能不能根据中序线索化的线索,来删除二叉树,不用后序递归呢?可以的。思路跟依据线索遍历二叉树的过程差不多。
依旧给出了后序递归删除线索二叉树的函数代码,deleteRecurse,经测试是正确的。
所有函数,由main调用。
再补充一点,线索化后的二叉树,并非完全意义上的双向链表,后者每个链表节点都有指针指向其前驱节点和后继节点,而线索化二叉树办不到,比如分支节点有俩子节点时候,我们的节点结构体没有多余的成员再存储它的前驱和后继信息。
main函数所在源文件代码:
#include<iostream>
#include<stdio.h>
using namespace std;
#define STACKDEPTH 15
enum Tag { pointer, thread };
struct ThreadedBiTreeNode {
char value;
Tag leftPtTag, rightPtTag;
ThreadedBiTreeNode* leftChild;
ThreadedBiTreeNode* rightChild;
};
extern void createBiTree(ThreadedBiTreeNode*& threadedBiTreeRoot, char* ptChar);
extern void displayBiTree(ThreadedBiTreeNode*& threadedBiTreeRoot);
extern void inOrderNoRecursion(ThreadedBiTreeNode*& threadedBiTreeRoot);
extern void threadBiTree(ThreadedBiTreeNode*& threadedBiTreeRoot);
extern void inOrderThreadBiTree(ThreadedBiTreeNode*& threadedBiTreeRoot);
extern void inOrderThreadBiTreeRecurse(ThreadedBiTreeNode*& threadedBiTreeRoot);
extern void rightRootLeftOrderThreadedBiTree(ThreadedBiTreeNode*& threadedBiTreeRoot);
extern void deleteRecurse(ThreadedBiTreeNode*& threadedBiTreeRoot);
extern void deleteThread(ThreadedBiTreeNode*& threadedBiTreeRoot);
int main() {
char array[] = "A(B(D(,G)),C(E,F))";
ThreadedBiTreeNode* threadedBiTreeRoot = NULL;
createBiTree(threadedBiTreeRoot,array);
cout << "the char array is :";
for (int i = 0; array[i] != '\0'; i++)
cout << array[i];
cout<< endl<< "binary tree is :";
displayBiTree(threadedBiTreeRoot);
cout << endl;
cout << "in order no recursion ,result : ";
inOrderNoRecursion(threadedBiTreeRoot);
cout << endl;
threadBiTree(threadedBiTreeRoot);
cout << "in order traverse threaded biTree : ";
inOrderThreadBiTree(threadedBiTreeRoot);
cout << endl;
cout << "in order Threaded BiTree,recursion : ";
inOrderThreadBiTreeRecurse(threadedBiTreeRoot);
cout << endl;
cout << "right -> root -> left order : ";
rightRootLeftOrderThreadedBiTree(threadedBiTreeRoot);
cout << endl;
// deleteRecurse(threadedBiTreeRoot);
deleteThread(threadedBiTreeRoot);
cout << "delete biTree ! ";
return 0;
}
其他被调用函数所在源文件代码如下:
#include<iostream>
#include<stdio.h>
using namespace std;
#define STACKDEPTH 15
enum Tag {pointer,thread};
struct ThreadedBiTreeNode {
char value;
Tag leftPtTag,rightPtTag;
ThreadedBiTreeNode* leftChild;
ThreadedBiTreeNode* rightChild;
};
void createBiTree(ThreadedBiTreeNode*& threadedBiTreeRoot, char* ptChar) {
struct {
ThreadedBiTreeNode* ptsBiTree[STACKDEPTH];
int indexTop = -1;
}sequStack;
ThreadedBiTreeNode* ptNew = NULL;
char s;
int leftRight;//1 is left 2 is right
while (*ptChar != '\0') {
s = *ptChar;
if ('A' <= s && s <= 'Z') {
ptNew = new ThreadedBiTreeNode;
ptNew->value = s;
ptNew->leftPtTag = ptNew->rightPtTag = pointer;
ptNew->leftChild = ptNew->rightChild = NULL;
if (threadedBiTreeRoot == NULL)
threadedBiTreeRoot = ptNew;
else if (leftRight == 1)
sequStack.ptsBiTree[sequStack.indexTop]->leftChild = ptNew;
else if (leftRight == 2)
sequStack.ptsBiTree[sequStack.indexTop]->rightChild = ptNew;
}
else if (s == '(') {
sequStack.indexTop++;
sequStack.ptsBiTree[sequStack.indexTop] = ptNew;
leftRight = 1;
}
else if (s == ',')
leftRight = 2;
else if (s == ')')
sequStack.indexTop--;
ptChar++;
}
}
void displayBiTree(ThreadedBiTreeNode*& threadedBiTreeRoot) { // 本查找方法是先序遍历
if (threadedBiTreeRoot == NULL)
return;//if binary tree does not exsit,return
cout << threadedBiTreeRoot->value;
if (threadedBiTreeRoot->leftChild != NULL || threadedBiTreeRoot->rightChild != NULL) {
cout << '(';
displayBiTree(threadedBiTreeRoot->leftChild);
if (threadedBiTreeRoot->rightChild != NULL) {
cout << ',';
displayBiTree(threadedBiTreeRoot->rightChild);
}
cout << ')';
}
}
void inOrderNoRecursion(ThreadedBiTreeNode*& threadedBiTreeRoot) {
if (threadedBiTreeRoot == NULL)
return;
ThreadedBiTreeNode* arrayPt[STACKDEPTH];
ThreadedBiTreeNode* ptNode;
int indexTop = 0;
arrayPt[indexTop] = threadedBiTreeRoot;
bool checkLeft = true;
while (indexTop >= 0) {
ptNode = arrayPt[indexTop];
while (checkLeft && ptNode->leftChild != NULL) {
indexTop++;
arrayPt[indexTop] = ptNode->leftChild;
ptNode = ptNode->leftChild;
}//find the most left leaf node of this tree
cout << ptNode->value;
indexTop--;//print the root node in the top of the array(stack)
checkLeft = false;
if (ptNode->rightChild != NULL) {
indexTop++;
arrayPt[indexTop] = ptNode->rightChild;
checkLeft = true;
}
}
}
void threadBiTree(ThreadedBiTreeNode*& threadedBiTreeRoot) {
if (threadedBiTreeRoot == NULL)
return;
static ThreadedBiTreeNode* ptPrior = NULL;
threadBiTree(threadedBiTreeRoot->leftChild);
if (threadedBiTreeRoot->leftChild == NULL) {
threadedBiTreeRoot->leftPtTag = thread;
threadedBiTreeRoot->leftChild = ptPrior;
}
if (ptPrior != NULL && ptPrior->rightChild == NULL) {
ptPrior->rightPtTag = thread;
ptPrior->rightChild = threadedBiTreeRoot;
}
ptPrior = threadedBiTreeRoot;
threadBiTree(threadedBiTreeRoot->rightChild);
}
void inOrderThreadBiTree(ThreadedBiTreeNode*& threadedBiTreeRoot) {
// no recursion method,the biTree is already threaded
ThreadedBiTreeNode* ptNode = threadedBiTreeRoot;
while (ptNode != NULL) {
while (ptNode->leftPtTag != thread)
ptNode = ptNode->leftChild;
cout << ptNode->value;
while (ptNode->rightPtTag == thread) {
ptNode = ptNode->rightChild;
cout << ptNode->value;
}
ptNode = ptNode->rightChild;
}
}
//this function we want to confirm that whether we can use reursion method
//to traverse BiTree or not .
void inOrderThreadBiTreeRecurse(ThreadedBiTreeNode*& threadedBiTreeRoot) {
if (threadedBiTreeRoot == NULL)
return;
if(threadedBiTreeRoot->leftPtTag == pointer)
inOrderThreadBiTreeRecurse(threadedBiTreeRoot->leftChild);
cout << threadedBiTreeRoot->value;
if(threadedBiTreeRoot->rightPtTag == pointer)
inOrderThreadBiTreeRecurse(threadedBiTreeRoot->rightChild);
}
void rightRootLeftOrderThreadedBiTree(ThreadedBiTreeNode*& threadedBiTreeRoot) {
// no recursion method,the biTree is already threaded
ThreadedBiTreeNode* ptNode = threadedBiTreeRoot;
while (ptNode != NULL) {
while (ptNode->rightPtTag != thread && ptNode->rightChild != NULL)
ptNode = ptNode->rightChild;//中序遍历最后一个字符的右指针指向空
//且其标志仍为pointer,为特殊情况
cout << ptNode->value;
while (ptNode->leftPtTag == thread && ptNode->leftChild != NULL) {
ptNode = ptNode->leftChild;
cout << ptNode->value;
}
ptNode = ptNode->leftChild;
}
}
void deleteRecurse(ThreadedBiTreeNode*& threadedBiTreeRoot) {
if (threadedBiTreeRoot == NULL)
return;
if (threadedBiTreeRoot->leftPtTag == pointer)
deleteRecurse(threadedBiTreeRoot->leftChild);
if (threadedBiTreeRoot->rightPtTag == pointer &&
threadedBiTreeRoot->rightChild != NULL)
deleteRecurse(threadedBiTreeRoot->rightChild);
delete(threadedBiTreeRoot);
}
void deleteThread(ThreadedBiTreeNode*& threadedBiTreeRoot) {
ThreadedBiTreeNode* ptNode = threadedBiTreeRoot,*ptRight;
while (ptNode != NULL) {
while (ptNode->leftPtTag != thread)
ptNode = ptNode->leftChild;
while (ptNode->rightPtTag == thread ) {//已经考虑了中序遍历最后的
ptRight = ptNode->rightChild; // 一个节点的右子节点为空,且
delete ptNode; //右子节点标志为指针非线索的情形
ptNode = ptRight;
}
ptRight = ptNode->rightChild;
delete ptNode;
ptNode = ptRight;
}
}
测试结果和对应的二叉树如下:
谢谢阅读