数据结构(5.3_6)——在线索二叉树中寻找前驱后继

中序线索二叉树找中序后继

  1. 若p->ratg==1,则next=p->rchild
  2. 若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);
    }

先序线索二叉树找先序后继

  1. 若p->ratg==1,则next=p->rchild
  2. 若p->rtag==0

在先序线索二叉树中找到指定结点*p的先序后继next

  1. 如果*p有左孩子,那么先序后继就是左孩子。
  2. 如果*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

  1. 若p->latg==1,则pre=p->lchild
  2. 若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

  1. 若p->ratg==1,则next=p->rchild
  2. 若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;
}

总结:

 

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值