中序线索二叉树找中序后继
- 若p->ratg==1,则next=p->rchild
- 若p->rtag==0
重点:后继=p的右子树中最左下的结点
代码:
//找到以P为根的子树,第一个被中序遍历的结点
ThreadNode* Firstnode(ThreadNode* p) {
//循环找到最左下结点(不一定是叶结点)
while (p->ltag == 0)
p = p->lchild;
return p;
}
//在中序线索二叉树中找到结点p的后继结点
ThreadNode* NextNode(ThreadNode* p) {
//右子树中最左下结点
if (p->rtag == 0)
return Firstnode(p->lchild);
else return
p->rchild;//rtag==1直接返回后继线索
}
对中序线索二叉树进行中序遍历(空间复杂度:O(1)):
void visit(ThreadNode* q) {
if (q->lchild == NULL) { // 左子树为空,建立前驱线索
q->lchild = pre;
q->ltag = 1;
}
if (pre != NULL && pre->rchild == NULL) { // 建立前驱结点的后继线索
pre->rchild = q;
pre->rtag = 1;
}
pre = q; // 更新前驱为当前结点
}
//找到以P为根的子树,第一个被中序遍历的结点
ThreadNode* Firstnode(ThreadNode* p) {
//循环找到最左下结点(不一定是叶结点)
while (p->ltag == 0)
p = p->lchild;
return p;
}
//在中序线索二叉树中找到结点p的后继结点
ThreadNode* NextNode(ThreadNode* p) {
//右子树中最左下结点
if (p->rtag == 0)
return Firstnode(p->lchild);
else return
p->rchild;//rtag==1直接返回后继线索
}
//对中序线索二叉树进行中序遍历(利用线索实现的非递归算法)
void Inorder(ThreadNode* T) {
for (ThreadNode* p = Firstnode(T); p != NULL; p = NextNode(p))
visit(p);
}
中序线索二叉树找中序前驱
重点:前驱=p的左子树中最右下的结点
//找到以P为根的子树,最后一个被中序遍历的结点
ThreadNode* Lastnode(ThreadNode* p) {
//循环找到最右下结点(不一定是叶结点)
while (p->rtag == 0)
p = p->rchild;
return p;
}
//在中序线索二叉树中找到结点p的前驱结点
ThreadNode* NextNode(ThreadNode* p) {
//左子树中最右下结点
if (p->rtag == 0)
return Lastnode(p->lchild);
else return
p->lchild;//ltag==1直接返回前驱线索
}
对中序线索二叉树进行逆向中序遍历
//找到以P为根的子树,最后一个被中序遍历的结点
ThreadNode* Lastnode(ThreadNode* p) {
//循环找到最右下结点(不一定是叶结点)
while (p->rtag == 0)
p = p->rchild;
return p;
}
//在中序线索二叉树中找到结点p的前驱结点
ThreadNode* NextNode(ThreadNode* p) {
//左子树中最右下结点
if (p->rtag == 0)
return Lastnode(p->lchild);
else return
p->lchild;//ltag==1直接返回前驱线索
}
//对中序线索二叉树进行逆向中序遍历(利用线索实现的非递归算法)
void RevInorder(ThreadNode* T) {
for (ThreadNode* p = Lastnode(T); p != NULL; p = NextNode(p))
visit(p);
}
先序线索二叉树找先序后继
- 若p->ratg==1,则next=p->rchild
- 若p->rtag==0
在先序线索二叉树中找到指定结点*p的先序后继next
- 如果*p有左孩子,那么先序后继就是左孩子。
- 如果*p没有左孩子,那么先序后继可能是:
- 如果*p有右孩子,则先序后继是右孩子。
- 如果p没有右孩子,那么我们需要向上回溯,直到找到一个节点,它是其父节点的左孩子,那么该父节点就是p的先序后继。
ThreadNode* PreorderNext(ThreadNode* p) {
// 情况1: 如果p有左孩子,则返回左孩子
if (p->ltag == 0) {
return p->lchild;
}
// 情况2: 如果p没有左孩子
else {
// 如果p有右孩子,则返回右孩子
if (p->rtag == 0) {
return p->rchild;
}
// 如果p没有右孩子,则需要向上回溯
else {
// 当p是其父节点的左孩子时,返回父节点
while (p->rtag == 1) { // p没有右孩子
if (p->parent == NULL) { // 如果p是根节点,则没有后继
return NULL;
}
// 如果p是其父节点的左孩子,返回父节点
if (p->parent->lchild == p) {
return p->parent;
}
// 否则继续向上回溯
p = p->parent;
}
// 如果回溯过程中找到了有右孩子的节点,返回其右孩子
return p->rchild;
}
}
}
先序线索二叉树找先序前驱
在先序线索二叉树中找到指定结点*p的先序前驱pre
用三叉链表来找前驱(有父节点的二叉树)
- 如果能找到p 的父节点,且p是左孩子
- 如果能找到p的父节点,且p是右孩子,其左兄弟为空
- 如果能找到p的父节点,且p是右孩子,其左兄弟非空(找深度最深的一个节点)
- 如果p是根节点。则p没有先序前驱
示例代码:
typedef struct ThreadNode {
int data; // 节点数据
struct ThreadNode *lchild, *rchild; // 左右孩子指针
int ltag, rtag; // 左右线索标志
struct ThreadNode *parent; // 父节点指针
} ThreadNode;
// 找到先序后继节点
ThreadNode* PreorderNext(ThreadNode* p) {
// 情况1: 如果p是根节点,则没有先序后继
if (p->parent == NULL) {
return NULL;
}
// 情况2: 如果p是左孩子
else if (p->parent->lchild == p) {
return p->parent;
}
// 情况3: 如果p是右孩子,且左兄弟为空
else if (p->parent->rchild == p && p->parent->lchild == NULL) {
return p->parent;
}
// 情况4: 如果p是右孩子,且左兄弟非空
else if (p->parent->rchild == p && p->parent->lchild != NULL) {
// 找到左兄弟
ThreadNode* leftSibling = p->parent->lchild;
// 如果左兄弟有右孩子,那么后继是左兄弟的右孩子
if (leftSibling->rtag == 0) {
return leftSibling->rchild;
}
// 如果左兄弟没有右孩子,那么后继是左兄弟
else {
return leftSibling;
}
}
// 如果p没有父节点,则p是根节点,没有先序后继
return NULL;
}
后序线索二叉树找后序前驱
在后序线索二叉树中找到指定结点*p的后序前驱pre
- 若p->latg==1,则pre=p->lchild
- 若p->ltag==0
- 如果*p有右孩子,则后序前驱是右孩子。
- 如果p没有右孩子,但p有左孩子,则后序前驱是左孩子。
- 如果p既没有左孩子也没有右孩子,则p的后序前驱是p的父节点,除非p是父节点的右孩子且父节点没有右孩子,在这种情况下,后序前驱是p的父节点的父节点(即p的祖父节点)。
typedef struct ThreadNode {
int data; // 节点数据
struct ThreadNode *lchild, *rchild; // 左右孩子指针
int ltag, rtag; // 左右线索标志
struct ThreadNode *parent; // 父节点指针
} ThreadNode;
// 找到后序前驱节点
ThreadNode* PostorderPredecessor(ThreadNode* p) {
// 情况1: 如果p有右孩子,则后序前驱是右孩子
if (p->rtag == 0) {
return p->rchild;
}
// 情况2: 如果p没有右孩子,但p有左孩子,则后序前驱是左孩子
else if (p->ltag == 0) {
return p->lchild;
}
// 情况3: 如果p既没有左孩子也没有右孩子
else {
// 如果p是其父节点的左孩子,或者p是其父节点的右孩子但父节点有右孩子
if (p->parent->lchild == p || (p->parent->rchild == p && p->parent->rtag == 0)) {
return p->parent;
}
// 如果p是其父节点的右孩子且父节点没有右孩子
else if (p->parent->rchild == p && p->parent->rtag == 1) {
// 继续向上查找,直到找到一个节点,它是其父节点的左孩子
while (p->parent != NULL && p->parent->rchild == p) {
p = p->parent;
}
// 如果p不是根节点,返回父节点,否则返回NULL
return p->parent;
}
}
// 如果p是根节点,则没有后序前驱
return NULL;
}
后序线索二叉树找后序前驱
在后序线索二叉树中找到指定结点*p的后序后继next
- 若p->ratg==1,则next=p->rchild
- 若p->rtag==0
用三叉链表来找前驱(有父节点的二叉树)
- 如果能找p的父节点,且p是右孩子
- 如果能找p的父节点,且p是左孩子,其右兄弟为空
- 如果能找p的父节点,且p是左孩子,其右兄弟非空
- 如果p是根节点,则p没有后序后继
代码示例:
typedef struct ThreadNode {
int data; // 节点数据
struct ThreadNode *lchild, *rchild, *parent; // 左孩子、右孩子和父节点的指针
} ThreadNode;
// 找到后序前驱节点
ThreadNode* PostorderPredecessor(ThreadNode* p) {
// 情况1: 如果p是根节点,则没有后序前驱
if (p->parent == NULL) {
return NULL;
}
// 情况2: 如果p是右孩子
else if (p->parent->rchild == p) {
return p->parent;
}
// 情况3: 如果p是左孩子,且没有右兄弟
else if (p->parent->lchild == p && p->rchild == NULL) {
return p->parent;
}
// 情况4: 如果p是左孩子,且有右兄弟
else if (p->parent->lchild == p && p->rchild != NULL) {
// 找到右兄弟
ThreadNode* rightSibling = p->rchild;
// 找到右兄弟的后序遍历的最后一个节点(即右兄弟的左子树最右下的节点)
while (rightSibling->lchild != NULL || rightSibling->rchild != NULL) {
if (rightSibling->rchild != NULL) {
rightSibling = rightSibling->rchild;
} else {
rightSibling = rightSibling->lchild;
}
}
return rightSibling;
}
// 如果没有其他情况,则返回NULL
return NULL;
}
总结: