第五章 常见的二叉树:线索二叉树
1 定义
线索二叉树相比普通二叉树而言,就是利用了普通二叉树的空闲内存,记录了某些结点的前趋和后继元素信息,存在n个结点的二叉链表必定存在n + 1个空指针域,线索二叉树就是利用这n+1个空间来存储
2 特点
(1)利用了这些浪费的空指针域
(2)利用好前趋结点和后继结点的信息,可以更快的进行前,中,后序等遍历
(3)特殊的双向链表:假如A指向B,A存的B孩子节点地址,B存的可能是后继节点A的地址,这种双向指针的类型是不一样的,可能是孩子节点指针,可能是线索指针
3 线索化二叉树
原图:^表示空指针,还没被利用
3.1 前序线索化二叉树
前序线索化:根-》左孩子-》右孩子,后继节点利用右孩子空指针,前驱节点利用左孩子空指针:绿色箭头是后继线索指针,指向后继节点;黄色箭头是前趋线索指针,指向前趋节点
3.2 中序线索化二叉树
中序线索化:左孩子-》根-》右孩子,后继节点利用右孩子空指针,前驱节点利用左孩子空指针:绿色箭头是后继线索指针,指向后继节点;黄色箭头是前趋线索指针,指向前趋节点
3.3 后序线索化二叉树
中序线索化:左孩子-》右孩子-》根,后继节点利用右孩子空指针,前驱节点利用左孩子空指针:绿色箭头是后继线索指针,指向后继节点;黄色箭头是前趋线索指针,指向前趋节点
4 代码实现
public class ThreadedBinaryTree {
//前趋元素
private Element preElement;
//元素存储结构
static class Element {
//数据域
String data;
//左指针域
Element left;
//右指针域
Element right;
//父元素,后续遍历才用到
Element parent;
//左指针域类型,false:左指针指向子节点、true:左指针指向前趋或者后继元素
boolean isLeftThread = false;
//右指针域类型,false:右指针指向子节点、true:右指针指向前趋或后继元素
boolean isRightThread = false;
Element(String data) {
this.data = data;
}
/**
* 通过数组构造一个二叉树(完全二叉树),
这也前面为什么说完全二叉树才适合用顺序
存储结构存储
* @param arr
* @param i
* @return
*/
static Element createBinaryTree(String[] arr, int i) {
Element element = null;
if(i < arr.length) {
element = new Element(arr[i]);
element.left = createBinaryTree(arr, i * 2 + 1);
element.right = createBinaryTree(arr, i * 2 + 2);
}
return element;
}
/**
* 线索化二叉树:中序
* @param element
*/
void inThreadedOrder(Element element) {
if(element == null) {
return;
}
//线索化左子树
inThreadedOrder(element.left);
//当前元素左指针为空,将左指针指向前驱元素
if(element.left == null) {
element.left = preElement;
element.isLeftThread = true;
}
//前驱元素的右指针为空,将它的右指针指向当前元素
if(preElement != null && preElement.right == null) {
preElement.right = element;
preElement.isRightThread = true;
}
//当前元素处理完后,作为前趋元素
preElement = element;
//线索化右子树
inThreadedOrder(element.right);
}
/**
* 遍历线索二叉树:中序,按照后继方式遍历
* @param element
*/
void inThreadedList(Element element) {
//找中序遍历方式开始的节点:最左孩子
while(element != null && !element.isLeftThread) {
element = element.left;
}
while(element != null) {
System.out.print(element.data + ", ");
//如果右指针是线索
if(element.isRightThread) {
element = element.right;
}
//如果右指针不是线索,找到右子树开始的元素
else
{
element = element.right;
while(element != null && !element.isLeftThread)
{
element = element.left;
}
}
}
}
/**
* 线索化二叉树:前序
* @param element
*/
void preThreadedOrder(Element element) {
if(element == null) {
return;
}
//左指针为空,将左指针指向前趋势元素
if(element.left == null) {
element.left = preElement;
element.isLeftThread = true;
}
//前趋元素的右指针为空的,右指针指向当前元素
if(preElement != null && preElement.right == null) {
preElement.right = element;
preElement.isRightThread = true;
}
//将当前元素作为前趋元素
preElement = element;
//线索化左子树
if(!element.isLeftThread) {
preThreadedOrder(element.left);
}
//线索化右子树
if(!element.isRightThread) {
preThreadedOrder(element.right);
}
}
/**
* 遍历线索二叉树:前序(按照后继线索遍历)
* @param element
*/
void preThreadedList(Element element) {
while(element != null) {
while(!element.isLeftThread) {
System.out.print(element.data + ", ");
element = element.left;
}
System.out.print(element.data + ", ");
element = element.right;
}
}
/**
* 线索化二叉树:后序
* @param element 节点
*/
void postThreadedOrder(Element element) {
if(element == null) {
return;
}
//处理左子树
postThreadOrder(element.left);
//处理右子树
postThreadOrder(element.right);
//左指针为空,将左指针指向前趋元素
if(element.left == null) {
element.left = preElement;
element.isLeftThread = true;
}
//前趋元素的后继指向当前节点
if(preElement != null && preElement.right == null) {
preElement.right = element;
preElement.isRightThread = true;
}
preElement = element;
}
/**
* 遍历线索二叉树:后续,按照后继方式遍历
* @param element
*/
void postThreadedList(element root) {
//找后序遍历方式开始的元素
Element element = root;
while(element != null && !element.isLeftThread) {
element = element.left;
}
Element preElement = null;
while(element != null) {
//右元素是线索
if(element.isRightThread) {
System.out.print(element.data + ", ");
preElement = element;
element = element.right;
}
else
{
//如果上个处理的元素是当前元素的右元素
if(element.right == preElement)
{
System.out.print(element.data + ", ");
if(element == root) {
return;
}
preElement = element;
element = element.parent;
}
//如果从左元素的进入则找到右子树的最左元素
else
{
element = element.right;
while(element != null && !element.isLeftThread) {
element = element.left;
}
}
}
}
}
}
}