线索化概念
当某节点的左指针为空时,令该指针指向按照某种方式遍历二叉树时得到该节点的前驱结点;当某节点的右指针为空时,令该指针指向按照某种方式遍历二叉树时得到该节点的后继结点。
但是无法区分:
- 左指针指向的结点是左孩子结点还是前驱结点
- 右指针指向的结点是右孩子结点还是后继结点
因此需要增加两个线索标志位来区分这两种情况:
leftThread(左线索):
- 0: leftChild 指向结点的左孩子
- 1: leftChild 指向结点某种遍历方式下前驱结点
rightThread(右线索):
- 0: rightChild 指向结点的右孩子
- 1: rightChild 指向结点某种遍历方式下后继结点
结点中指向前驱结点和后继结点的指针称为线索,二叉树结点加上线索的二叉树称为线索二叉树,对二叉树以某种方式(前序、中序、后续)遍历使其变为线索二叉树的过程称为按照该方法对二叉树进行线索化。
线索化二叉树
ThreadTree.h
#pragma once
//执行效率
//空间占用
//代码简单
#include <stddef.h>
typedef char ThreadType;
//定义一个枚举类型用来表示某一节点的左右指针是子树还是线索
typedef enum Flag{
CHILD,
THREAD,
}Flag;
typedef struct ThreadNode{
ThreadType data;
struct ThreadNode* left;
struct ThreadNode* right;
//引入两个 flag 来分别表示左右指针指向的是子树还是线索
Flag lflag;
Flag rflag;
}ThreadNode;
ThreadNode* ThreadTreeCreate(ThreadType array[], size_t size, ThreadType invalid);
void PreThreading(ThreadNode* root); //先序线索
void PreOrderByThreading(ThreadNode* root); //先序遍历
void InThreading(ThreadNode* root); //中序线索
void InOrderByThreading(ThreadNode* root); //中序遍历
void PostThreading(ThreadNode* root); //后序线索
void PostOrderByThreading(ThreadNode* root); //后序遍历
typedef struct TreeNode{
char data;
struct TreeNode* left;
struct TreeNode* right;
struct TreeNode* prev;
struct TreeNode* next;
}TreeNode;
ThreadTree.c
#include "ThreadTree.h"
#include <stdlib.h>
#include <stdio.h>
ThreadNode* CreateThreadNode(ThreadType value){
ThreadNode* new_node = (ThreadNode*)malloc(sizeof(ThreadNode));
new_node->data = value;
new_node->left = NULL;
new_node->right = NULL;
new_node->lflag = CHILD;
new_node->rflag = CHILD;
return new_node;
}
// ABD##EG###C#F##
ThreadNode* _ThreadTreeCreate(ThreadType array[], size_t size, size_t* index, ThreadType invalid){
if(index == NULL || *index >= size){
return NULL;
}
if(array[*index] == invalid){
return NULL;
}
ThreadNode* root = CreateThreadNode(array[*index]);
++(*index);
root->left = _ThreadTreeCreate(array, size, index, invalid);
++(*index);
root->right = _ThreadTreeCreate(array, size, index, invalid);
return root;
}
// 输入的array是二叉树的先序遍历结果(带有空指针域)
// ABD##EG###C#F##
// invalid表示用哪个特殊字符表示空节点。此处使用#
ThreadNode* ThreadTreeCreate(ThreadType array[], size_t size, ThreadType invalid){
size_t index = 0;
return _ThreadTreeCreate(array, size, &index, invalid);
}
void _PreThreading(ThreadNode* root, ThreadNode** prev){
if(root == NULL || prev == NULL){
return;
}
//处理根节点
//1.如果当前子树的根节点的左子树为空,就把 left 指针指向前驱
if(root->left == NULL){
root->left = *prev;
root->lflag = THREAD;
}
//2.如果当前子树的根节点的前驱的右子树为空,就把前驱的 right 指针指向根节点
if(*prev != NULL && (*prev)->right == NULL){
(*prev)->right = root;
(*prev)->rflag = THREAD;
}
*prev = root;
//处理左子树
if(root->lflag == CHILD){
_PreThreading(root->left, prev);
}
//处理右子树
if(root->rflag == CHILD){
_PreThreading(root->right, prev);
}
return;
}
void PreThreading(ThreadNode* root){
ThreadNode* prev = NULL;
_PreThreading(root, &prev);
}
void PreOrderByThreading(ThreadNode* root){
if(root == NULL){
return;
}
ThreadNode* cur = root;
while(cur != NULL){
while(cur->lflag == CHILD){
printf("%c ", cur->data);
cur = cur->left;
}
printf("%c ", cur->data);
//无论 cur 的 right 指针指向后继还是指向右子树,都被下面的逻辑覆盖到了
cur = cur->right;
}
printf("\n");
return;
}
void _InThreading(ThreadNode* root, ThreadNode** prev){
if(root == NULL || prev == NULL){
return;
}
//处理左子树
if(root->lflag == CHILD){
_InThreading(root->left, prev);
}
//处理根节点
if(root->left == NULL){
root->left = *prev;
root->lflag = THREAD;
}
if(*prev != NULL && (*prev)->right == NULL){
(*prev)->right = root;
(*prev)->rflag = THREAD;
}
*prev = root;
//处理右子树
if(root->rflag == CHILD){
_InThreading(root->right, prev);
}
}
void InThreading(ThreadNode* root){
ThreadNode* prev = NULL;
_InThreading(root, &prev);
}
void InOrderByThreading(ThreadNode* root){
//1.定义cur指针指向root,找到最左侧的结点,并且在寻找的路径上不能访问
if(root == NULL){
return;
}
ThreadNode* cur = root;
//当循环结束,cur就指向了这棵树的最左侧节点
while(cur != NULL && cur->lflag == CHILD){
cur = cur->left;
}
//2.进入循环,访问cur结点,如果cur节点为空,说明遍历完了
while(cur != NULL){
printf("%c ", cur->data);
if(cur->rflag == THREAD){
//3.如果cur的right刚好是线索,cur指向cur->right
cur = cur->right;
}else{
//4.如果cur的right是子树,需要让cur指向cur->right这个子树的最左侧节点
cur = cur->right;
while(cur != NULL && cur->lflag == CHILD){
cur = cur->left;
}
}
}
printf("\n");
return;
}
void _PostThreading(ThreadNode* root, ThreadNode** prev){
if(root == NULL || prev == NULL){
return;
}
//处理左子树
if(root->lflag == CHILD){
_PostThreading(root->left, prev);
}
//处理右子树
if(root->rflag == CHILD){
_PostThreading(root->right, prev);
}
//处理根节点
if(root->left == NULL){
root->left = *prev;
root->lflag = THREAD;
}
if(*prev != NULL && (*prev)->right == NULL){
(*prev)->right = root;
(*prev)->rflag = THREAD;
}
*prev = root;
}
void PostThreading(ThreadNode* root){
ThreadNode* prev = NULL;
_PostThreading(root, &prev);
}
void _PreOrderConvertToList(TreeNode* root, TreeNode** prev){
if(root == NULL || prev == NULL){
return;
}
//处理根节点
root->prev = *prev;
if(*prev != NULL){
(*prev)->next = root;
}
*prev = root;
//处理左子树
_PreOrderConvertToList(root->left, prev);
//处理右子树
_PreOrderConvertToList(root->right, prev);
}
void PreOrderConvertToList(TreeNode* root){
TreeNode* prev = NULL;
_PreOrderConvertToList(root, &prev);
}