数据结构-线索二叉树

线索二叉树是利用空出来的指针域来存储 某个遍历序列 下的前驱和后继,左边的空节点指针指向前驱节点,右边的空节点指针指向后继节点。

由于线索二叉树是基于某种遍历序列的,所以就需要在对二分搜索树进行某种遍历的时候,构造线索,主要分为:

  • 前序序列线索树
  • 中序序列线索树
  • 后续序列线索树

用土办法找树中某节点的中序遍历前驱节点

//前向指针
Node<E> pre = null;
boolean findFlag = false;
Node<E> target = null;

private Node<E> findNode(E e) {
    return findNode(e, root);
}

private Node<E> findNode(E e, Node<E> node) {
    if (node == null) {
        return null;
    }
    if (e.compareTo(node.data) < 0) {
        return findNode(e, node.left);
    } else if (e.compareTo(node.data) > 0) {
        return findNode(e, node.right);
    }
    return node;
}

//中序遍历
public E findInOrderPre(E e) { 
    //找到目标节点
    target = findNode(e);
    
    return pre.data;
}

//按照前序的顺序遍历整棵树
private void inOrderForFindPre(Node<E> node) {
    if (node == null) {
        return;
    }
    inOrderForFindPre(node.left);
    //在这里调整pre指针
    visit(node);
    inOrderForFindPre(node.right);
}

private void visit(Node<E> node) {
    //如果还没有找到
    if (!findFlag) {
        //如果当前节点就是目标节点
        if (node.data.compareTo(q.data) == 0) {
            findFlag = true;
            //输出pre,即前序遍历中 目标节点的前驱节点
            System.out.println("目标节点的前驱节点为:" + pre);
        } else {
            //否则,将pre指向当前节点,继续向后遍历
            pre = node;
        }
    }
}

节点结构

private static class Node<E extends Comparable<E>> {
    E data;
    Node<E> left;
    Node<E> right;
    
    //用来记录当前节点的左右子节点指针是指向真实的子节点还是作为线索指针
    //0-指向真实的子节点 1-线索指针
    int ltag;
    int rtag; 
}

构造前序线索树

private Node<E> preOrderThreadPrev;
public void preThreadTree() {
    preThreadTree(root);
    if (preOrderThreadPrev != null) {
        preOrderThreadPrev.rtag = 1;
    }
}
private void preThreadTree(Node<E> node) {
    if (node == null) {
        return;
    }
    preThreadTreeVisit(node);
    if (node.ltag == 0) {
        preThreadTree(node.left);
    }
    if (node.rtag == 0) {
        preThreadTree(node.right);
    }
}

private void preThreadTreeVisit(Node<E> node) {
    if (node.left == null) {
        node.left = preOrderThreadPrev;
        node.ltag = 1;
    }

    //如果前驱的right是空,就让他指向node节点
    if (preOrderThreadPrev != null &&
            preOrderThreadPrev.right == null) {
        preOrderThreadPrev.right = node;
        preOrderThreadPrev.rtag = 1;
    }
    preOrderThreadPrev = node;
}

遍历前序线索树

/**
 * ③由于先序遍历线索树中的节点无法找到它的前驱节点(因为前驱是向前指,而节点的当前两个子节点都是向后只的)
 *   所以只能从前往后遍历
 * */
private Node<E> preOrderSequenceFirstNode(Node<E> node) {
    //以node为根节点的子树的先序遍历的第一个节点就是当前节点
    return node;
}
private Node<E> preOrderSequenceNextNode(Node<E> node) {
    //如果right被线索化了,就直接返回right
    if (node.rtag == 1) {
        return node.right;
    }
    //如果没有,先序遍历的后继节点 只可能是它的左 / 右子节点
    if (node.left != null) {
        return node.left;
    } else if (node.right != null) {
        return node.right;
    }
    return null;
}
public void preSequenceOrder() {
    for (Node<E> cur = preOrderSequenceFirstNode(root); cur != null; cur = preOrderSequenceNextNode(cur)) {
        System.out.print(cur.data + " ");
    }
    System.out.println();
}

构造中序线索树

private Node<E> inOrderThreadPrev;
//②中序序列线索化
public void inThreadTree() {
    inThreadTree(root);
    if (inOrderThreadPrev != null) {
        inOrderThreadPrev.rtag = 1;
    }
}
private void inThreadTree(Node<E> node) {
    if (node == null) {
        return;
    }
    inThreadTree(node.left);
    inThreadTreeVisit(node);
    if (node.rtag == 0) {
        inThreadTree(node.right);
    }
}

private void inThreadTreeVisit(Node<E> node) {
    if (node.left == null) {
        node.left = inOrderThreadPrev;
        node.ltag = 1;
    }

    if (inOrderThreadPrev != null &&
            inOrderThreadPrev.right == null) {
        inOrderThreadPrev.right = node;
        inOrderThreadPrev.rtag = 1;
    }
    inOrderThreadPrev = node;
}

遍历中序线索树

private Node<E> inOrderSequenceFirstNode(Node<E> node) {
    Node<E> cur = node;
    while (cur.ltag == 0) {
        cur = cur.left;
    }
    return cur;
}
//中序遍历序列中的下一个节点
private Node<E> inOrderSequenceNextNode(Node<E> node) {
    if (node.rtag == 1) {
        return node.right;
    }

    return inOrderSequenceFirstNode(node.right);
}

public void inSequenceOrder() {
    for (Node<E> cur = inOrderSequenceFirstNode(root); cur != null; cur = inOrderSequenceNextNode(cur)) {
        System.out.print(cur.data + " ");
    }
    System.out.println();
}


/**
 * 中序遍历序列的逆序遍历
 * */
private Node<E> inOrderSequenceLastNode(Node<E> node) {
    Node<E> cur = node;
    while (cur.rtag == 0) {
        cur = cur.right;
    }
    return cur;
}
private Node<E> inOrderSequencePrevNode(Node<E> node) {
    if (node.ltag == 1) {
        return node.left;
    }
    return inOrderSequenceLastNode(node.left);
}
public void inSequenceOrderReverse(){
    for (Node<E> cur = inOrderSequenceLastNode(root); cur != null; cur = inOrderSequencePrevNode(cur)) {
        System.out.print(cur.data + " ");
    }
    System.out.println();
}

构造后续线索树

private Node<E> postOrderThreadPrev;
public void postThreadTree() {
    postThreadTree(root);

    //注意,由于后续遍历的最后一个节点不是叶子节点,所以不能同先序遍历和
    //中序遍历那样,存在下面的逻辑
    /*if (postOrderThreadPrev != null) {
        postOrderThreadPrev.rtag = 1;
    }*/
}
private void postThreadTree(Node<E> node) {
    if (node == null) {
        return;
    }
    //if (node.ltag == 0) {
        postThreadTree(node.left);
    //}
    //if (node.rtag == 0) {
        postThreadTree(node.right);
    //}
    postThreadTreeVisit(node);

}
private void postThreadTreeVisit(Node<E> node) {
    if (node.left == null) {
        node.left = postOrderThreadPrev;
        node.ltag = 1;
    }

    if (postOrderThreadPrev != null
            && postOrderThreadPrev.right == null) {
        postOrderThreadPrev.right = node;
        postOrderThreadPrev.rtag = 1;
    }
    postOrderThreadPrev = node;
}

遍历后续线索树

/**
 * ③遍历
 * 由于后序线索二叉树找不到后继节点,所以只能从后往前遍历
 * */
private Node<E> postOrderSequenceLastNode(Node<E> node) {
    return node;
}

private Node<E> postOrderSequencePreNode(Node<E> node) {
    if (node.ltag == 1) {
        return node.left;
    }

    if (node.right != null && node.rtag != 1) {
        return node.right;
    }
    return node.left;
}

public void postSequenceOrderReverse() {
    for (Node<E> cur = postOrderSequenceLastNode(root); cur != null; cur = postOrderSequencePreNode(cur)) {
        System.out.print(cur.data + " ");
    }
    System.out.println();
}

为了在Windows安装ADB工具,你可以按照以下步骤进行操作: 1. 首先,下载ADB工具包并解压缩到你自定义的安装目录。你可以选择将其解压缩到任何你喜欢的位置。 2. 打开运行窗口,可以通过按下Win+R键来快速打开。在运行窗口中输入"sysdm.cpl"并按下回车键。 3. 在系统属性窗口中,选择"高级"选项卡,然后点击"环境变量"按钮。 4. 在环境变量窗口中,选择"系统变量"部分,并找到名为"Path"的变量。点击"编辑"按钮。 5. 在编辑环境变量窗口中,点击"新建"按钮,并将ADB工具的安装路径添加到新建的路径中。确保路径正确无误后,点击"确定"按钮。 6. 返回到桌面,打开命令提示符窗口。你可以通过按下Win+R键,然后输入"cmd"并按下回车键来快速打开命令提示符窗口。 7. 在命令提示符窗口中,输入"adb version"命令来验证ADB工具是否成功安装。如果显示版本信息,则表示安装成功。 这样,你就成功在Windows安装ADB工具。你可以使用ADB工具来执行各种操作,如枚举设备、进入/退出ADB终端、文件传输、运行命令、查看系统日志等。具体的操作方法可以参考ADB工具的官方文档或其他相关教程。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [windows环境安装adb驱动](https://blog.youkuaiyun.com/zx54633089/article/details/128533343)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Windows安装使用ADB简单易懂教程](https://blog.youkuaiyun.com/m0_37777700/article/details/129836351)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

echo20222022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值