数据结构之二叉树篇卷四 -- 二叉树线索化(With Java)
一、线索二叉树简介
二叉树本身是一种非线性结构,然而当你对二叉树进行遍历时,你会发现遍历结果是一个线性序列。这个序列中的节点存在前驱后继关系。因此,如何将这种前驱后继信息赋予给原本的二叉树呢?这就是二叉树的线索化过程。所以可以通过丰富原有的二叉树构建一棵可以知道结点的前驱后继的新的二叉树,我们叫它线索二叉树。
二、构建线索二叉树
2.1 定义线索二叉树节点
public class ThreadNode<E> {
public E data;
public ThreadNode<E> lnode;
public ThreadNode<E> rnode;
enum tag {CHILD, THREAD};
public tag ltag;
public tag rtag;
public ThreadNode(E data){
this.data = data;
ltag = tag.CHILD;
rtag = tag.CHILD;
}
public ThreadNode(){}
}
为了充分利用二叉树的空指针域,我们可以将结点线索依附在这些空指针中。这样一来,在我们访问根节点的左右节点时,我们必须要区分什么时候是线索,什么时候子女指针。因此为每个节点设置两个标志位。
2.2 建立线索二叉树
在遍历二叉树的时候顺便进行线索化。线索化就是每遇到一个节点存在空指针域就要立即填上它的前驱(左指针空)或者后继(右指针空)。
/**
* 利用中序非递归遍历对二叉树进行线索化
* @author sheepcore
*/
public void createInThread(){
if(root == null)
return;
ThreadNode<E> pre = null;
ThreadNode<E> cur = root;
MyStack<ThreadNode<E>> stack = new MyStack<>();
while (cur != null || !stack.isEmpty()) {
while (cur != null){
stack.push(cur);
cur = cur.lnode;
}
if(!stack.isEmpty()){
cur = stack.pop();
if(cur.lnode == null) { //当前节点的前驱作为线索
cur.lnode = pre;
cur.ltag = ThreadNode.tag.THREAD;
}
if(pre != null && pre.rnode == null){
pre.rnode = cur; //当前节点的前驱的后继节点是当前节点
pre.rtag = ThreadNode.tag.THREAD;
}
pre = cur;
if(cur.rnode != null){
cur = cur.rnode;
}
else
cur = null; } //if
} //while
}
2.3 遍历线索二叉树
以中序线索二叉树的遍历为例。getFirst(root) 和 getNext(root) 分别是获取中序遍历中以 root 为根的第一个访问的节点和 root 的后继节点。这两个辅助函数在最后面的完整代码有实现。
public void inOrder(ThreadNode<E> root){
ThreadNode<E> cur;
for (cur = getFirst(root); cur != null; cur = getNext(cur)) {
visit(cur);
}
}
2.4 完整代码
public class ThreadTree <E> {
private ThreadNode<E> root;
/**
* using for construct a binary tree with its element as an integer
*/
public ArrayList<Integer> list;
public ThreadNode<E> getRoot() { return root; }
public ThreadTree(){
list = new ArrayList<>();
}
/**
* build a binary tree in a preorder way recursively
* @return root
* @author sheepcore
*/
public ThreadNode<E> generate() {
if(list.isEmpty())
return null;
int data = list.get(0);
list.remove(0);
ThreadNode<E> node;
if(data != -1) {
node = (ThreadNode<E>) new ThreadNode<Integer>(data);
node.lnode = generate(); //generate left subtree
node.rnode = generate(); //generate right subtree
return node;
}
else
return null;
}
/**
* build a binary tree in preorder
* @param treeSequence "1 2 -1 -1 3 -1 -1 "
*/
public void preOderBuild(String treeSequence) {
String[] nodes = treeSequence.split("\\s+");
for (int i = 0; i < nodes.length; i++) {
list.add(Integer.parseInt(nodes[i]));
}
root = generate();
}
/**
* 利用中序非递归遍历对二叉树进行线索化
* @author sheepcore
*/
public void createInThread(){
if(root == null)
return;
ThreadNode<E> pre = null;
ThreadNode<E> cur = root;
MyStack<ThreadNode<E>> stack = new MyStack<>();
while (cur != null || !stack.isEmpty()) {
while (cur != null){
stack.push(cur);
cur = cur.lnode;
}
if(!stack.isEmpty()){
cur = stack.pop();
if(cur.lnode == null) { //当前节点的前驱作为线索
cur.lnode = pre;
cur.ltag = ThreadNode.tag.THREAD;
}
if(pre != null && pre.rnode == null){
pre.rnode = cur; //当前节点的前驱的后继节点是当前节点
pre.rtag = ThreadNode.tag.THREAD;
}
pre = cur;
if(cur.rnode != null){
cur = cur.rnode;
}
else
cur = null;
} //if
} //while
}
public ThreadNode<E> getFirst(ThreadNode<E> root){
ThreadNode<E> cur = root;
while (cur != null && cur.ltag == ThreadNode.tag.CHILD){
cur = cur.lnode;
}
return cur;
}
public ThreadNode<E> getLast(ThreadNode<E> root){
ThreadNode<E> cur = root;
while (cur != null && cur.rtag == ThreadNode.tag.CHILD){
cur = cur.rnode;
}
return cur;
}
public ThreadNode<E> getNext(ThreadNode<E> root){
ThreadNode<E> cur = root;
if(cur.rtag == ThreadNode.tag.THREAD)
return cur.rnode;
else
return getFirst(cur.rnode);
}
public ThreadNode<E> getPrior(ThreadNode<E> root){
ThreadNode<E> cur = root;
if(cur.ltag == ThreadNode.tag.THREAD)
return cur.lnode;
else
return getLast(cur.lnode);
}
public void visit(ThreadNode<E> root){
if(root != null && root.data != null){
System.out.print(root.data);
System.out.print("\t");
}
}
/**
* inorder traversal
* @param root
* @author sheepcore
*/
public void inOrder(ThreadNode<E> root){
ThreadNode<E> cur;
for (cur = getFirst(root); cur != null; cur = getNext(cur)) {
visit(cur);
}
}
public static void main(String[] args) {
String str = "0 1 -1 3 -1 -1 2 4 -1 -1 -1";
ThreadTree<Integer> threadTree = new ThreadTree<>();
threadTree.preOderBuild(str);
threadTree.createInThread();
threadTree.inOrder(threadTree.getRoot());
}
}
数据结构之二叉树篇卷四 -- 二叉树线索化(With Java)的更多相关文章
- 算法与数据结构(三) 二叉树的遍历及其线索化(Swift版)
前面两篇博客介绍了线性表的顺序存储与链式存储以及对应的操作,并且还聊了栈与队列的相关内容.本篇博客我们就继续聊数据结构的相关东西,并且所涉及的相关Demo依然使用面向对象语言Swift来表示.本篇博客 ...
- 数据结构之二叉树篇卷三 -- 二叉树非递归遍历(With Java)
Nonrecursive Traversal of Binary Tree First I wanna talk about why we should <code>Stack</c ...
- 数据结构之二叉树篇卷二 -- 二叉树递归遍历(With Java)
一.先序递归遍历(Preorder Recursive Traversal) 1.1 算法 首先需要明确的是这里的序是针对 root 节点而言的.故先序即先“访问”根节点,其次“访问”其左右节点. 1 ...
- 【数据结构&算法】12-线索二叉树
目录 前言 线索二叉树的概念 线索二叉树的实现 线索二叉树的寻点思路二 类双向链表参考图 参考代码 中序遍历线索化 前言 在<大话数据结构>P190 页中有一句话:其实线索二叉树,就等于是 ...
- 遍历二叉树 traversing binary tree 线索二叉树 threaded binary tree 线索链表 线索化
遍历二叉树 traversing binary tree 线索二叉树 threaded binary tree 线索链表 线索化 1. 二叉树3个基本单元组成:根节点.左子树.右子树 以L.D.R ...
- 【java 数据结构】还不会二叉树?一篇搞定二叉树
二叉树是我们常见的数据结构之一,在学习二叉树之前我们需要知道什么是树,什么是二叉树,本篇主要讲述了二叉树,以及二叉树的遍历. 你能get到的知识点? 1.树的介绍 2.二叉树的介绍 3.二叉树遍历的四 ...
- c++(排序二叉树线索化)
前面我们谈到了排序二叉树,还没有熟悉的同学可以看一下这个,二叉树基本操作.二叉树插入.二叉树删除1.删除2.删除3.但是排序二叉树也不是没有缺点,比如说,如果我们想在排序二叉树中删除一段数据的节点怎么 ...
- JS数据结构第五篇 --- 二叉树和二叉查找树
一.二叉树的基本概念 从逻辑结构角度来看,前面说的链表.栈.队列都是线性结构:而今天要了解的“二叉树”属于树形结构. 1.1 多叉树的基本概念,以上图中“多叉树”为例说明 节点:多叉树中的每一个点都叫 ...
- JS数据结构第六篇 --- 二叉树力扣练习题
1.第226题:翻转二叉树 递归+迭代两种实现方式: /** 反转二叉树 * Definition for a binary tree node. * function TreeNode(val) { ...
随机推荐
- 深度学习笔记(十四)车道线检测 SCNN
论文:Spatial As Deep: Spatial CNN for Traffic Scene Understanding 代码:https://github.com/XingangPan/SCN ...
- 消费端ACK和重回队列
使用场景 消费端ACK和重回队列 消费端ACK使用场景: 1.消费端进行消费的时候,如果由于业务异常我们可以进行日志记录,然后进行补偿. 2.由于服务器宕机等严重问题,那我们就需要手工进行ACK保障消 ...
- 「CF525D」Arthur and Walls
题目链接 戳我 \(Solution\) 如果一个#要更改,那么一个四个格子的正方形只有他一个是#,bfs弄一下就好了 \(Code\) #include<bits/stdc++.h> u ...
- Python代码整洁之道(一)
很多新手在开始学一门新的语言的时候,往往会忽视一些不应该忽视的细节,比如变量命名和函数命名以及注释等一些内容的规范性,久而久之养成了一种习惯.对此呢,我特意收集了一些适合所有学习 Python 的人, ...
- leetcode题目11.盛最多水的容器(中等)
题目描述: 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其 ...
- LeetCode 137. 只出现一次的数字 II(Single Number II)
题目描述 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? 示例 1: ...
- 石川es6课程---11、json
石川es6课程---11.json 一.总结 一句话总结: ` 感觉更方便了一点,增加了一些简写 ` key-value 一样时可以简写:console.log({ a,b}}) ` 里面函数可以简写 ...
- 在linux下通过ssh运行X图形软件
服务器端:编辑/etc/ssh/sshd_config中的以下内容启用AllowTcpForwarding启用X11Forwarding将X11DisplayOffset设定为10. 启用X11Use ...
- Java工具类-HttpUtil
package com.sh.util; import java.io.IOException; import java.io.UnsupportedEncodingException; import ...
- DP&图论 DAY 2 下午
DP&图论 DAY 2 下午 基础树形DP 前言◦ 1:与树或图的生成树相关的动态规划.◦ 2:以每棵子树为子结构,在父亲节点合并,注意树具有天然的子结构.这是很优美的很利于dp的.◦ 3 ...