本文章是我看完《大话数据结构》总结的,加了自己的理解和代码
本文章适合稍微了解线索二叉树的人
线索二叉树
- 线索二叉树相当于双向链表,知道一个结点便可得出前驱和后继(这里指按某种遍历的前驱和后继)
- 如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继,那么采用线索二叉链表的存储结构就是非常不错的选择
- 注意:先有一颗完整的树才能进行线索化
- 线索化指充分利用原来指向为NULL的,如果其为左孩子或右孩子,则
- 左孩子指向前驱
- 右孩纸指向后继
- 线索二叉树
struct TreeNode {
int val;
TreeNode* lchild;
TreeNode* rchild;
bool lTag = Link;//Link 表示左右孩纸指针,Thread 表示指向前驱或者后继的线索
bool rTag = Link;//初始化默认为Link
}
中序线索二叉树
中序线索化
class Solution {
TreeNode* pre = NULL;//属性全局变量,指向前驱
void inorderThread(TreeNode* cur) {
if (cur == NULL) return;
inorderThead(cur->lchild);
if (cur->lchild == NULL) {//没有左孩子,线索化,指向前驱pre
cur->lTag == Thread;
cur->lchild = pre;
}
if (pre != NULL && pre->rchild == NULL) {//前驱没有后孩子,那么指向后继(即现在访问的)
pre->rchild = cur;
pre->rTag = Thread;
}
pre = cur;//更新
inorderThead(cur->rchild);
}
};
- 经过线索化后,会有两个NULL,一个是中序遍历第一个结点的左孩子,和最后一个结点的右孩子
中序线索化加虚拟头节点
class Solution {
TreeNode* dummyHead = new TreeNode;
dummyHead->lchild == cur;//虚拟头结点的左孩子指向根结点
void inorderAddThread(TreeNode* cur) {
if (cur == NULL) return;
inorderAddThread(cur->lchild);
//令中序遍历第一个结点的 左孩子指向虚拟头节点
if (cur->lchild == NULL) {
cur->lTag = Thread;
cur->lchild = dummyHead;
}
//令中序遍历的最后一个结点的右孩子指向虚拟头节点,且头节点的右孩纸指向最后一个结点
if (cur->rchild == NULL) {
cur->rTag = Thread;
cur->rchild = dummyHead;
dummyHead->rchild = cur;
}
inorderAddThread(cur->rchild);
}
};
迭代顺序遍历中序线索二叉树
- 其实是模拟递归,先到最左,但是因为迭代无法回溯,利用后驱遍历下一个(即中间),然后再转右孩子。本质是模拟递归
- 迭代顺序遍历利用的是后继
顺序遍历:从第一个结点顺后继继续遍历
class Solution {
vector<TreeNode*> inorderTraversal(TreeNode* dummyHead) {
vector<TreeNode*> result;
//指向根节点
ThreeNode* cur = dummyHead->lchild;
while (cur != dummyHead) {
//找到中序遍历第一个结点(向左找到无左孩子的结点)
while (cur->lTag == Link){
cur = cur->lchild;
}
result.push_back(cur);
//利用后继指针遍历下一个结点
while (cur->rTag == Thread && cur->rchild != dummyHead) {
cur = cur->rchild;
result.push_back(cur);
}
//转向右孩子
cur = cur->rchild;
}
return result;
}
}
迭代倒序遍历中序线索二叉树
- 迭代倒序遍历利用的是前驱
倒序遍历:从最后一个结点起顺前驱进行遍历
class Solution {
vector<TreeNode*> inorderTraversal(TreeNode* dummyHead) {
vector<TreeNode*> result;
//指向根节点
ThreeNode* cur = dummyHead->lchild;
//或者直接cur = dummyHead->rchild;//直接指向中序遍历倒数第一个结点
while (cur != dummyHead) {
//向右到没有右的地方
while (cur->rTag == Link){
cur = cur->rchild;
}
result.push_back(cur);
//利用前驱指向前一个
while (cur->lTag == Thread && cur->lchild != dummyHead) {
cur = cur->lchild;
result.push_back(cur);
}
//跳到左孩子
cur = cur->lchild;
}
return result;
}