知识补充:什么是中序线索化
中序遍历是什么
一、代码解释
1.结构体定义
Node 结构体:
成员说明:
int data:存储节点的数据值。
struct Node* lchild:该节点的左孩子
struct Node* rchild:该节点的右孩子。
int ltag 和 int rtag:用于标记左孩子和右孩子指针的性质,0 表示指向真正的子节点,1 表示指向该节点在中序遍历中的前驱或后继节点。
作用:表示二叉树中的一个节点。
QueueNode 结构体:
成员说明:
Node* treeNode:存储一个二叉树节点的指针。
struct QueueNode* next:指向队列中下一个元素。
作用:用于构建队列,辅助层次构建二叉树。
Queue 结构体:
成员说明:
QueueNode* front:指向队列的头部。
QueueNode* rear:指向队列的尾部。
作用:表示一个队列。
2.队列操作函数createQueue
createQueue 函数:
使用 malloc 分配内存以存储 Queue 结构体。
检查分配内存是否成功,若失败则调用 exit(1) 终止程序。
初始化队列的 头front 和 尾rear 指针为 NULL,表示队列为空。
返回指向新创建队列的指针。
作用:创建一个新的空队列。
3.队列的入队操作enqueue
enqueue 函数
参数:
Queue* q:指向队列的指针。
Node* treeNode:指向要插入队列的二叉树节点的指针。
动态分配内存以存储一个新的队列节点。
检查内存分配是否成功,若失败则直接返回,不进行后续操作。
将传入的二叉树节点的地址赋值给新队列节点的 treeNode 成员,建立关联。
初始化新队列节点的 next 指针为 NULL,表示其后没有其他节点。
判断队列是否为空(即尾指针 rear 是否为 NULL),若是空队列,则将新节点同时作为队头和队尾。
若队列不为空,则将原队尾节点的 next 指针指向新节点,并更新队尾指针为新节点。
作用:将一个二叉树节点插入到队列中。
4.队列的出队操作dequeue
dequeue 函数
参数:Queue* q,指向队列的指针。
作用:从队列中移除并返回队头的二叉树节点。
检查队列是否为空,若为空则返回 NULL,表示没有节点可以出队。
将队头节点保存到临时指针 temp 中,以便后续释放内存。
获取队头节点所关联的二叉树节点,并保存到 treeNode 中,这是要返回的结果。
将队头指针移动到下一个节点,实现队头的前进。
检查队列是否变为空,若变为空则将队尾指针也设置为 NULL。
释放之前保存的队头节点所占用的内存,避免内存泄漏。
返回出队的二叉树节点。
5.判断队列是否为空函数isEmpty
isEmpty 函数
参数:Queue* q,指向队列的指针。
返回值:如果队列为空,返回非零值(真);否则返回 0(假)。
实现:通过检查队列的 front 指针是否为 NULL 来判断队列是否为空。在队列的链式存储结构中,当 front 指针为 NULL 时,表示队列中没有任何节点,即队列为空。
作用:判断队列是否为空。
6.释放队列函数freeQueue
freeQueue 函数
参数:Queue* q,指向要释放的队列的指针。
作用:释放队列及其包含的所有节点所占用的内存。
while (!isEmpty(q)):使用一个循环,只要队列不为空就持续执行。
在循环体内调用 出队dequeue(q) 函数,不断移除队列中的节点并释放每个节点的内存。
这个过程会一直持续到队列被完全清空为止。
free(q):在队列为空后,释放队列本身的内存空间。
7.创建中序线索二叉树createInThreadedTree
createInThreadedTree 函数
作用:对整个二叉树进行中序线索化。
参数:Node* root,指向二叉树的根节点。
①创建一个指针变量 pre,初始化为 NULL。这个变量将用于记录中序遍历过程中的前一个节点。
②调用中序线索化函数 inThreading,从根节点开始对整个二叉树进行中序线索化,并传入 pre 的地址,以便在递归过程中能够记录和更新前驱节点
8.二叉树节点创建createNode
createNode 函数
作用:创建一个新的二叉树节点。
参数:int data,表示要存储在新节点中的数据值。
返回值:返回指向新创建的二叉树节点的指针。如果内存分配失败,则返回 NULL。
①使用 malloc 函数动态分配内存,为新的二叉树节点分配足够的空间。sizeof(Node) 计算出 Node 结构体的大小。
②检查内存分配是否成功。如果 newNode 为 NULL,表示内存分配失败,程序调用 exit(1) 终止执行。
③将传入的 data 值赋给新节点的 data 成员。
④初始化新节点的左右孩子指针为 NULL,表示该节点目前没有子节点。
⑤初始化新节点的左右标签为 0,表示左右指针目前指向的是实际的子节点(尽管此时子节点还未创建)。
⑥返回指向新创建节点的指针。
9.中序线索化操作inThreading
inThreading 函数
作用:对二叉树进行中序线索化。
参数:
Node* root:指向当前要线索化的节点。
Node** pre:指向一个指针变量,该变量保存了中序遍历过程中上一个访问的节点。
①如果当前节点为空,直接返回。因为空节点无需线索化。
②递归对当前节点的左子树进行中序线索化。这一步符合中序遍历的顺序,先处理左子树。
③如果当前节点没有左孩子,则将当前节点的左指针指向中序遍历中的前驱节点(即 pre 指向的节点),并将左标签 ltag 设置为 1,表示这个左指针是线索而不是指向实际的左孩子。
④如果前驱节点存在且前驱节点没有右孩子,则将前驱节点的右指针指向当前节点,并将前驱节点的右标签 rtag 设置为 1,表示这个右指针是线索而不是指向实际的右孩子。
⑤更新 pre 指针,使其指向当前节点,表示当前节点已经成为下一个节点的前驱。
⑥递归对当前节点的右子树进行中序线索化。这一步符合中序遍历的顺序,在处理完左子树和当前节点后,最后处理右子树。
10.查找前驱和后继节点操作findPredecessor&findSuccessor
findPredecessor 函数
作用:找到指定节点在中序遍历下的前驱节点。
参数:Node* node,指向要查找前驱的节点。
返回值:返回指向中序前驱节点的指针。如果没有前驱,则返回 NULL。
findSuccessor 函数
作用:找到指定节点在中序遍历下的后继节点。
参数:Node* node,指向要查找后继的节点。
返回值:返回指向中序后继节点的指针。如果没有后继,则返回 NULL。
findPre:
①如果节点的左标签为1,说明其左指针是指向前驱的线索,直接返回该左指针。
②如果左标签不是1,则从该节点的左孩子开始查找。
③沿着左孩子的右子树一直向下寻找,直到找到一个右标签为1的节点或者无法继续。
④返回找到的前驱节点。
findSuc:
①如果节点的右标签为1,说明其右指针指向后继的线索,直接返回该右指针。
②如果右标签不是1,则从该节点的右孩子开始查找。
③沿着右孩子的左子树一直向下寻找,直到找到一个左标签为1的节点或者无法继续。
④返回找到的后继节点。
11. 中序遍历printInOrderSequece
printInOrderSequence 函数
作用:中序遍历线索二叉树并打印节点数据。
参数:Node* root,指向二叉树的根节点。
①如果根节点为空,直接返回,结束函数执行。
②创建一个指针变量 current,初始化为根节点,用于遍历树。
③将 current 移动到最左节点。因为线索二叉树的最左节点是中序遍历的第一个节点。
④开始主循环,循环条件是 current 不为 NULL。
⑤打印当前节点的数据。
⑥如果当前节点的右标签为1,说明右指针是指向后继的线索。
⑦移动 current 到后继节点。
⑧如果右标签不是1,说明右指针指向实际的右孩子。
⑨移动 current 到右孩子。
⑩将 current 移动到其右子树的最左节点,这是为了找到下一个中序遍历的节点。
12.查询前驱和后继节点findNodeByValue
findNodeByValue 函数
作用:在中序线索二叉树中查找具有指定值的节点。
参数:
Node* root:指向二叉树的根节点。
int value:要查找的值。
返回值:如果找到具有指定值的节点,返回指向该节点的指针;否则返回 NULL。
①如果根节点为空,直接返回 NULL,因为空树中不存在任何节点。
②创建一个指针变量 current,初始化为根节点,用于遍历树。
③用while循环将 current 移动到树的最左节点。这一步确保从树的中序遍历的起始位置开始查找。
④开始主循环,循环条件是 current 不为 NULL。
⑤检查当前节点的数据是否等于要查找的值。如果是,直接返回当前节点指针。
⑥如果当前节点的右标签为1,说明右指针是指向中序后继的线索。
移动 current 到后继节点。
⑦ 如果右标签不是1,说明右指针指向实际的右孩子。
移动 current 到右孩子。
将 current 移动到其右子树的最左节点,以便继续中序遍历。
⑧如果循环结束后仍未找到匹配的节点,返回 NULL。
13.安全释放二叉树freeTree
freeTree 函数
作用:递归释放二叉树中所有节点所占用的内存,防止内存泄漏。
参数:Node* root,指向二叉树的根节点。
①如果根节点为空,直接返回。因为空树无需释放内存。
②如果当前节点的左标签为0,说明左指针指向实际的左孩子节点,递归释放左子树的内存。
③如果当前节点的右标签为0,说明右指针指向实际的右孩子节点,递归释放右子树的内存。
④释放当前节点的内存。
14.用户输入二叉树操作bulidTreeFromInput
buildTreeFromInput 函数
作用:根据用户输入构建二叉树。
返回值:返回指向构建好的二叉树根节点的指针。如果用户输入-1,则返回 NULL,表示不构建树。
①声明一个整型变量 value,用于存储用户输入的节点值。
提示用户输入根节点的值。
②读取用户输入。如果输入失败或者用户输入-1,则返回 NULL,表示不构建树。
③创建根节点。
创建一个队列,用于辅助构建树。
将根节点入队。
④当队列不为空时,循环执行以下操作:
取出队列中的节点。
提示用户输入当前节点左子节点的值。
读取用户输入。
如果输入的值不是-1,则创建左子节点,并将其入队。
提示用户输入当前节点右子节点的值。
读取用户输入。
如果输入的值不是-1,则创建右子节点,并将其入队。
⑤释放队列资源。
⑥返回根节点指针。
15.主函数逻辑
1.循环构建和处理二叉树:使用 while (1) 创建一个无限循环,允许用户反复构建和处理二叉树,直到用户选择结束程序。
2.打印菜单:通过 printf 输出菜单信息,提示用户当前操作是中序线索二叉树操作。
3.构建二叉树:
调用 buildTreeFromInput 函数根据用户输入构建二叉树。
如果用户输入-1,表示不构建树,则打印“程序结束。”并退出循环。
4.线索化二叉树:
调用 createInThreadedTree 函数对构建好的二叉树进行中序线索化。
5.中序遍历并打印:
打印提示信息“中序遍历结果:”。
调用 printInOrderSequence 函数进行中序遍历并打印结果。
6.查询节点:
使用嵌套的 while (1) 循环,反复提示用户输入要查询的节点值。
如果用户输入-1,退出查询循环。
调用 findNodeByValue 函数查找指定值的节点。
如果找不到节点,打印提示信息并跳过本次循环。
调用 findPredecessor 和 findSuccessor 函数查找节点的前驱和后继。
打印前驱和后继的信息,使用三元运算符处理空节点的情况,使其显示为“无”。
7.释放内存:
调用 freeTree 函数释放二叉树占用的内存。
打印提示信息,告知用户内存已释放,准备新一轮输入。
8.结束程序
二、完整代码
#include <stdio.h>
#include <stdlib.h>typedef struct Node
{int data;struct Node* lchild;struct Node* rchild;int ltag;int rtag;
} Node;// 队列结构用于层次构建二叉树
typedef struct QueueNode
{Node* treeNode;struct QueueNode* next;
} QueueNode;typedef struct Queue
{QueueNode* front;QueueNode* rear;
} Queue;// 队列操作函数
Queue* createQueue()
{Queue* q = (Queue*)malloc(sizeof(Queue));if (q == NULL){exit(1);}q->front = q->rear = NULL;return q;
}void enqueue(Queue* q, Node* treeNode)
{QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));if (newNode == NULL){return;}newNode->treeNode = treeNode;newNode->next = NULL;if (q->rear == NULL){q->front = q->rear = newNode;}else{q->rear->next = newNode;q->rear = newNode;}
}Node* dequeue(Queue* q)
{if (q->front == NULL) return NULL;QueueNode* temp = q->front;Node* treeNode = temp->treeNode;q->front = q->front->next;if (q->front == NULL)q->rear = NULL;free(temp);return treeNode;
}int isEmpty(Queue* q)
{return q->front == NULL;
}void freeQueue(Queue* q)
{while (!isEmpty(q)) {dequeue(q);}free(q);
}// 二叉树节点创建
Node* createNode(int data)
{Node* newNode = (Node*)malloc(sizeof(Node));if (newNode == NULL){exit(1);}newNode->data = data;newNode->lchild = newNode->rchild = NULL;newNode->ltag = newNode->rtag = 0;return newNode;
}// 中序线索化
void inThreading(Node* root, Node** pre)
{if (!root)return;inThreading(root->lchild, pre);if (!root->lchild) {root->lchild = *pre;root->ltag = 1;}if (*pre && !(*pre)->rchild) {(*pre)->rchild = root;(*pre)->rtag = 1;}*pre = root;inThreading(root->rchild, pre);
}void createInThreadedTree(Node* root)
{Node* pre = NULL;inThreading(root, &pre);
}// 查找前驱和后继
Node* findPredecessor(Node* node)
{if (node->ltag == 1) return node->lchild;Node* p = node->lchild;while (p && p->rtag == 0) p = p->rchild;return p;
}Node* findSuccessor(Node* node)
{if (node->rtag == 1)return node->rchild;Node* p = node->rchild;while (p && p->ltag == 0) p = p->lchild;return p;
}// 中序遍历线索二叉树
void printInOrderSequence(Node* root)
{if (!root) return;Node* current = root;while (current->ltag == 0) current = current->lchild;while (current){printf("%d ", current->data);if (current->rtag == 1) {current = current->rchild;}else {current = current->rchild;while (current && current->ltag == 0) {current = current->lchild;}}}
}// 通过值查找节点
Node* findNodeByValue(Node* root, int value)
{if (!root)return NULL;Node* current = root;while (current->ltag == 0)current = current->lchild;while (current) {if (current->data == value) return current;if (current->rtag == 1){current = current->rchild;}else {current = current->rchild;while (current && current->ltag == 0) {current = current->lchild;}}}return NULL;
}// 安全释放二叉树
void freeTree(Node* root)
{if (!root) return;if (root->ltag == 0)freeTree(root->lchild);if (root->rtag == 0)freeTree(root->rchild);free(root);
}// 用户输入构建二叉树
Node* buildTreeFromInput()
{int value;printf("输入根节点值(-1结束): ");if (scanf_s("%d", &value) != 1 || value == -1)return NULL;Node* root = createNode(value);Queue* q = createQueue();enqueue(q, root);while (!isEmpty(q)) {Node* current = dequeue(q);printf("输入节点%d的左子节点(-1为空): ", current->data);scanf_s("%d", &value);if (value != -1) {current->lchild = createNode(value);enqueue(q, current->lchild);}printf("输入节点%d的右子节点(-1为空): ", current->data);scanf_s("%d", &value);if (value != -1){current->rchild = createNode(value);enqueue(q, current->rchild);}}freeQueue(q);return root;
}int main()
{while (1){printf("\n===== 中序线索二叉树操作 =====\n");Node* root = buildTreeFromInput();if (!root) {printf("程序结束。\n");break;}createInThreadedTree(root);printf("中序遍历结果: ");printInOrderSequence(root);printf("\n");int query;while (1){printf("\n输入要查询的节点值(-1返回): ");scanf_s("%d", &query);if (query == -1)break;Node* target = findNodeByValue(root, query);if (!target) {printf("节点%d不存在!\n", query);continue;}Node* pred = findPredecessor(target);Node* succ = findSuccessor(target);printf("前驱: %s\t后继: %s\n",pred ? itoa(pred->data, (char[]) { 0 }, 10) : "无",succ ? itoa(succ->data, (char[]) { 0 }, 10) : "无");}freeTree(root);printf("内存已释放,准备新一轮输入...\n");}return 0;
}
该代码用c语言实现的——一个完整的中序线索二叉树工具,支持用户动态构建、线索化、遍历和查询功能,适合教学演示或需要快速查找前驱/后继节点的场景。数据结构!!!