今天学了数据结构中的线索二叉树-
线索:在传统的二叉树中,节点的左指针指向左子树,右指针指向右子树。如果节点没有左子树,则左指针指向该节点的中序前驱节点;如果没有右子树,则右指针指向中序后继节点。
线索二叉树的性质:线索二叉树通过这种方式使得遍历时不再需要使用栈或递归,能够直接按照前序、中序或后序进行非递归遍历。
2.线索二叉树的种类- 前序线索二叉树:在前序线索二叉树中,节点的左线索指向前驱节点,右线索指向后继节点。
中序线索二叉树:在中序线索二叉树中,左线索指向中序前驱,右线索指向中序后继。
后序线索二叉树:相对少用,节点的右线索指向后继,左线索指向前驱。
3.线索二叉树的构建- 创建线索二叉树的过程:需要遍历一棵普通的二叉树,在遍历过程中,通过保存前一个访问的节点来构造线索。在访问当前节点时,更新前驱和后继信息。
4.线索二叉树的遍历- 中序遍历:通过线索化的结构,可以实现中序遍历而无需使用栈或递归,从而提升遍历效率。
前序和后序遍历:线索二叉树也支持前序和后序遍历,但实现会更加复杂。
5. 应用场景- 避免栈空间:在进行树的遍历时,可以避免使用系统栈,因此适合于大规模树的遍历。
内存管理:通过线索化降低了对内存的需求,做到了树结构的优化。
6.线索化的标识- 通常会引入一个字段来标识指针是指向子节点还是线索。常见的做法是在节点结构中增加一个布尔值(如isThread,或isLeftThread和isRightThread)。
7.线索二叉树的缺点- 复杂性:相比普通的二叉树,为其添加线索会增加实现的复杂性。
不适合某些操作:某些情况下,线索化可能反而使得树的某些基本操作变得复杂。
8. 示例代码(中序线索二叉树的构建)
以下是一个简单的中序线索二叉树的节点结构和线索化的代码示例:
c#include
include <stdlib.h>
typedef struct ThreadNode {
int data; // 节点数据 struct ThreadNode* lchild; // 左孩子 struct ThreadNode* rchild; //右孩子 int ltag; // 左标记:0表示指向左孩子,1表示指向前驱 int rtag; //右标记:0表示指向右孩子,1表示指向后继} ThreadNode;
ThreadNode* pre = NULL; // 用于存储前一个访问的节点void createThreadTree(ThreadNode* root) {
if (root == NULL) return;
createThreadTree(root->lchild);
if (root->lchild == NULL) { // 如果没有左子树 root->ltag =1; // 设置左标记为线索 root->lchild = pre; // 左指针指向前驱 }
if (pre != NULL && pre->rchild == NULL) { //处理前驱 pre->rtag =1; // 设置右标记为线索 pre->rchild = root; //右指针指向当前节点 }
pre = root; // 更新前驱节点 createThreadTree(root->rchild);
}
// 中序遍历void inThreadTraversal(ThreadNode* root) {
ThreadNode* p = root;
while (p != NULL) {
// 找到最左边的节点 while (p->ltag ==0) {
p = p->lchild;
}
printf("%d ", p->data); //访问节点 // 如果有后继 while (p->rtag ==1 && p->rchild != NULL) {
p = p->rchild;
printf("%d ", p->data);
}
p = p->rchild; // 指向下一个节点 }
}