数据结构:线索二叉树

1.背景介绍
现有一棵结点数目为n的二叉树,采⽤二叉链表的形式存储,对于每个结点均有指向左右孩子的两个指针域。
结点为n的二叉树一共有n-1条有效分支路径。那么,则二叉链表中存在2n - ( n-1 ) = n + 1个空指针域。那么,这些空指针造成了空间浪费。
当对二叉树进行中序遍历时可以得到二叉树的中序序列。如图所示,二叉树的中序遍历结果为DBEAC,可以得知A的前驱结点为E,后继结点为C。
这种关系的获得是建⽴在完成遍历后得到的,那么可不可以在建⽴二叉树时就记录下前驱后继的关系呢,那么在后续寻找前驱结点和后继结点时将⼤⼤提升效率。
2.线索化
将某结点的空指针域指向该结点的前驱后继,定义规则如下:
若结点的左子树为空,则该结点的左孩子指针指向其前驱结点。
若结点的右子树为空,则该结点的右孩子指针指向其后继结点。
这种指向前驱和后继的指针称为线索。将一棵普通二叉树以某种次序遍历,并添加线索的过程称为线索化。

3.线索化的改进

可以将一棵二叉树线索化为一棵线索二叉树,那么新的问题产⽣了。我们如何区分一个结点的 lchild指针是指向左孩子还是前驱结点呢?
为了解决这一问题,现需要添加 标志位 ltag,rtag。并定义规则如下:
        ltag为0时,指向左孩子,为1时指向前驱
        rtag为0时,指向右孩子,为1时指向后继

 

在遍历过程中,如果当前结点没有左孩子,需要将该结点的 lchild 指针指向遍历过程中的前一个结点,所以在遍历过程中,要设置两个指针,一个指针(名为 pre ),时刻指向当前访问结点(cur)的前一个结点。
4. 线索化的优势
递归遍历需要使⽤系统栈,非递归遍历需要使⽤内存中的空间来帮助遍历,⽽线索化之后就不需 要这些辅助了,直接可以像遍历数组一样遍历。
线索二叉树核心目的在于加快查找结点的前驱和后继的速度。如果不使⽤线索的话,当查找一个 结点的前驱与后继需要从根节点开始遍历,当然,如果二叉树数据量较小时,可能线索化之后作⽤不大,但是当数据量很⼤时,线索化所带来的性能提升就会比较明显。
代码:
threadedB Tree.h
#ifndef THREADED_BTREE_H
#define THREADED_BTREE_H
typedef int Element;
// 线索二叉树的节点结构
typedef struct treeNode {
	Element data;
	struct treeNode *left;
	struct treeNode *right;
	int lTag;					// 0表示left指向左节点,1表示left指向前驱
	int rTag;					// 0表示right指向右节点,1表示right指向后继
}TBTNode;
// 二叉树的描述信息结构
typedef struct {
	TBTNode *root;			// 二叉树的根节点
	int count;				// 二叉树的节点个数
}ThreadedBTree;

ThreadedBTree *createThreadedBTree(TBTNode *root);
void releaseThreadedBTree(ThreadedBTree *tree);
TBTNode *createTBTNode(Element e);
void insertThreadedBTree(ThreadedBTree *tree, TBTNode *parent, TBTNode *left, TBTNode *right);
void visitTBTNode(TBTNode *node);

// 中序线索化树,在中序遍历的过程中,建立左右孩子为空指针的值的确定
void inOrderThreadedTree(ThreadedBTree *tree);
// 按照已经线索化后的树,进行中序遍历
void inOrderTBTree(ThreadedBTree *tree);
#endif

threadedB Tree.c

#include <stdio.h>
#include <stdlib.h>
#include "threadedBTree.h"

ThreadedBTree *createThreadedBTree(TBTNode *root) {
	ThreadedBTree *tree = (ThreadedBTree *) malloc(sizeof(ThreadedBTree));
	if (tree == NULL) {
		fprintf(stderr, "tree malloc failed!\n");
		return NULL;
	}
	if (root) {
		tree->root = root;
		tree->count = 1;
	} else {
		tree->root = NULL;
		tree->count = 0;
	}
	return tree;
}
// 后序遍历释放节点,但在找后继节点时,需要找非线索化的节点
static void freeTBTNode(ThreadedBTree *tree, TBTNode *node) {
	if (node) {
		if (node->lTag == 0) {
			freeTBTNode(tree, node->left);
		}
		if (node->rTag == 0) {
			freeTBTNode(tree, node->right);
		}
		free(node);
		tree->count--;
	}
}
/* 释放线索树 */
void releaseThreadedBTree(ThreadedBTree *tree) {
	if (tree) {
		freeTBTNode(tree, tree->root);
		printf("tree have %d node!\n", tree->count);
	}
}
/* 产生新节点,初始化左右指针为NULL */
TBTNode *createTBTNode(Element e) {
	TBTNode *node = (TBTNode *) malloc(sizeof(TBTNode));
	if (node == NULL) {
		fprintf(stderr, "tree node malloc failed!\n");
		return NULL;
	}
	node->data = e;
	node->left = node->right = NULL;
	node->lTag = node->rTag = 0;
	return node;
}
/* 树的节点访问 */
void visitTBTNode(TBTNode *node) {
	if (node) {
		printf("\t%c", node->data & 0xff);
	}
}

void insertThreadedBTree(ThreadedBTree *tree, TBTNode *parent, TBTNode *left, TBTNode *right) {
	if (tree && parent) {
		parent->left = left;
		parent->right = right;
		if (left)
			tree->count++;
		if (right)
			tree->count++;
	}
}
static TBTNode *pre = NULL;
static void inOrderThreading(TBTNode *node) {
	if (node) {
		inOrderThreading(node->left);
		if (node->left == NULL) {		// 更新当前节点的前驱
			node->lTag = 1;
			node->left = pre;
		}
		// 当前节点会不会是前面那个节点后继节点
		if (pre && pre->right == NULL) {
			pre->rTag = 1;
			pre->right = node;
		}
		pre = node;
		inOrderThreading(node->right);
	}
}

void inOrderThreadedTree(ThreadedBTree *tree) {
	if (tree) {
		inOrderThreading(tree->root);
	}
}

void inOrderTBTree(ThreadedBTree *tree) {
	TBTNode *node = tree->root;
	while (node) {
		while (node->lTag == 0) {
			node = node->left;
		}
		visitTBTNode(node);
		// 一直向右开始遍历,只要右边是后继标记,就查看
		while (node->rTag && node->right) {
			node = node->right;
			visitTBTNode(node);
		}
		node = node->right;
	}
}

main.c

#include <stdio.h>
#include "threadedBTree.h"

ThreadedBTree *initTree() {
	TBTNode *nodeA = createTBTNode('A');
	TBTNode *nodeB = createTBTNode('B');
	TBTNode *nodeC = createTBTNode('C');
	TBTNode *nodeD = createTBTNode('D');
	TBTNode *nodeE = createTBTNode('E');
	TBTNode *nodeF = createTBTNode('F');
	TBTNode *nodeG = createTBTNode('G');
	TBTNode *nodeH = createTBTNode('H');
	TBTNode *nodeK = createTBTNode('K');

	ThreadedBTree *tree = createThreadedBTree(nodeA);
	insertThreadedBTree(tree, nodeA, nodeB, nodeE);
	insertThreadedBTree(tree, nodeB, NULL, nodeC);
	insertThreadedBTree(tree, nodeE, NULL, nodeF);
	insertThreadedBTree(tree, nodeC, nodeD, NULL);
	insertThreadedBTree(tree, nodeF, nodeG, NULL);
	insertThreadedBTree(tree, nodeG, nodeH, nodeK);
	return tree;
}

int main() {
	ThreadedBTree *tree = initTree();
	// 1. 对原二叉树进行中序线索化,n + 1个空节点,按照左孩子指针指向前驱,右孩子指针指向后继
	inOrderThreadedTree(tree);
	// 2. 根据线索化后的树,进行中序遍历
	inOrderTBTree(tree);
	releaseThreadedBTree(tree);
	return 0;
}

在CLion上的运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值