目录
前言
A.建议
1.学习算法最重要的是理解算法的每一步,而不是记住算法。
2.建议读者学习算法的时候,自己手动一步一步地运行算法。
tips:文中的对数均以2为底数
B.简介
在C语言中,二叉树的中序线索化是一种将二叉树的中序遍历过程中的空指针(NULL)改为指向前驱或后继节点的操作。这种线索化可以方便地实现二叉树的中序遍历,而无需递归或使用栈。
一 代码实现
#include <stdio.h>
#include <stdlib.h>
// 定义二叉树节点结构
struct TreeNode {
int data;
struct TreeNode* left;
struct TreeNode* right;
int isThreaded; // 标记是否为线索
};
// 创建新的二叉树节点
struct TreeNode* createNode(int data) {
struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
if (newNode == NULL) {
perror("Memory allocation failed");
exit(EXIT_FAILURE);
}
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
newNode->isThreaded = 0;
return newNode;
}
// 中序线索化函数
void createInorderThread(struct TreeNode* root, struct TreeNode** prev) {
if (root == NULL) {
return;
}
// 递归线索化左子树
createInorderThread(root->left, prev);
// 处理当前节点
if (*prev != NULL) {
if ((*prev)->right == NULL) {
// 如果前驱节点的右指针为空,将其设置为当前节点
(*prev)->right = root;
(*prev)->isThreaded = 1;
}
}
// 更新前驱节点
*prev = root;
// 递归线索化右子树
createInorderThread(root->right, prev);
}
// 中序遍历线索化二叉树
void inorderThreadedTraversal(struct TreeNode* root) {
struct TreeNode* current = root;
while (current != NULL) {
while (current->left != NULL && current->isThreaded == 0) {
current = current->left;
}
printf("%d ", current->data);
// 如果右子树为空或是线索化的,移动到后继节点
if (current->right == NULL || current->isThreaded == 1) {
current = current->right;
} else {
// 否则,移动到右子树的最左节点
current = current->right;
while (current->left != NULL) {
current = current->left;
}
}
}
}
int main() {
// 创建示例二叉树
struct TreeNode* root = createNode(1);
root->left = createNode(2);
root->right = createNode(3);
root->left->left = createNode(4);
root->left->right = createNode(5);
// 执行中序线索化
struct TreeNode* prev = NULL;
createInorderThread(root, &prev);
// 执行中序遍历线索化二叉树
printf("Inorder Threaded Traversal: ");
inorderThreadedTraversal(root);
printf("\n");
return 0;
}
二 时空复杂度
A.时间复杂度分析:
创建二叉树节点:
时间复杂度:O(1)(常数时间),因为只是为每个节点分配内存。
中序线索化函数 (createInorderThread):每个节点都只会被访问一次。对于每个节点,进行常数次操作(检查前驱节点的右指针、设置前驱节点的右指针等)。因此,总体时间复杂度为 O(n),其中 n是二叉树中的节点数量。
中序遍历线索化二叉树函数 (inorderThreadedTraversal):
每个节点最多被访问两次:一次从左子树移动到当前节点,一次从当前节点移动到右子树。
对于每个节点,进行常数次操作。总体时间复杂度为O(n),其中 n 是二叉树中的节点数量。
主函数 (main):创建示例二叉树,线索化并中序遍历的时间复杂度为 O(n)。
因此,整个算法的时间复杂度为 O(n)。
B.空间复杂度分析:
递归调用栈:
递归调用 createInorderThread 和 inorderThreadedTraversal 函数时会使用调用栈。由于这是中序遍历,递归深度最多为树的高度h,最坏情况下为O(h)。
额外空间:
除了递归调用栈之外,算法的额外空间消耗主要是用于存储前驱节点指针。由于前驱节点指针的数量与节点数量成正比,所以空间复杂度为 O(n)。
因此,整个算法的空间复杂度为 O(h) + O(n)。在最坏情况下,当树为链状结构时,h = n,因此空间复杂度为 O(n)。
三 优缺点
A.优点:
中序遍历优化: 中序线索化二叉树的主要优点是在中序遍历时不需要递归或使用栈,节省了空间和提高了效率。这对于特定场景下需要频繁中序遍历的操作来说是一个优势。
简洁性: 代码相对简短且易于理解。它清晰地展示了二叉树节点的结构、创建和线索化的过程。
通用性: 该代码可以用于任意具有指向左右子树的指针的二叉树,不仅仅是二叉搜索树。
B.缺点:
内存泄漏风险: 在 createNode 函数中使用了 malloc 分配内存,但没有提供释放内存的函数。在实际应用中,应该添加一个函数来释放二叉树的所有节点,以避免内存泄漏。
错误处理不足: 在内存分配失败时,使用 perror 和 exit 终止程序,但这种方式并不友好,它直接终止整个程序而不提供清理资源的机会。更好的做法是在错误发生时返回错误代码或采用其他错误处理策略。
不支持动态插入和删除节点: 该代码仅在创建二叉树时进行中序线索化,不支持在后续操作中动态插入或删除节点。如果需要支持这些操作,需要相应的修改。
不考虑线程安全性: 该代码没有考虑多线程环境下的安全性。在多线程环境中,可能需要采用适当的同步机制来防止竞态条件。
局限性: 该代码实现的是中序线索二叉树,适用于中序遍历的优化。如果其他遍历方式也需要优化,需要相应地扩展实现。
四 现实中的应用
中序遍历优化: 中序线索化的一个主要应用是在二叉树的中序遍历操作中提供性能优势。在传统的中序遍历中,通常需要使用递归或栈来跟踪节点,而线索化后,可以直接按照线索进行遍历,无需额外的递归或栈,从而减少了空间开销和提高了遍历效率。
数据库索引: 在数据库的实现中,索引结构通常采用二叉树,如二叉搜索树(BST)。通过对BST进行中序线索化,可以优化索引的查找过程,减少数据库查询的时间复杂度。
文件系统: 文件系统中的目录结构通常是树形结构。中序线索化可以在文件系统的目录结构上提供更高效的遍历方式,减少文件查找的时间。
表达式树的求值: 在编译器中,表达式树用于表示算术表达式的结构,中序线索化可以在进行表达式树的中序遍历时提高求值的效率。
图形用户界面(GUI): 在GUI的布局中,控件(如按钮、文本框等)的排列通常使用树状结构。中序线索化可以在布局中提供更高效的遍历方式,用于实现一些特定的界面操作。
————————————————
版权声明:本文为CSDN博主「JJJ69」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_56154577/article/details/135716982