上一章介绍了二叉树的二叉链表的实现,并给出了相关遍历算法,但是,当以二叉链表作为二叉树的存储结构时,无法直接得到结点在任一序列中的前驱与后继信息。为了规避这个弊端,本章将引入线索二叉树的概念 ,并给出相关Java实现。
为了得到前驱与后继的信息,最直观的想法是在每个结点上添加两个指针域,分别指向结点在遍历时的前驱及后继。然而,这样会使存储密度大大降低,而在有n个结点的二叉树中有n+1个空链域,故可利用这些空链域来存放结点的前驱及后继信息,并作如下规定:若结点有左子树,则其leftChild域指向其左孩子,否则,令leftChild指向其前驱;若结点有右子树,则其rightChild域指向其右孩子,否则,令rightChild指向其后继。
为了避免混淆,现对上章中的结点结构作适当改动,改动后如下:
leftChilt | leftTag | element | rightTag | rightChild |
---|
并规定:leftTag取值为false时,指示结点的左孩子,取值为true时,指示结点的前驱;rightTag取值为false时,指示结点的右孩子,取值为true时,指示结点的后继。
线索链表:以上述结点结构构成的二叉链表作为二叉树的存储结构,其中指示结点前驱及后继信息的指针称作线索,加上线索的二叉树称为线索二叉树。
线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程。
线索化的实质:修改空指针(将二叉链表中的空指针改为指向前驱或后继的线索)。
下面给出相关java代码实现:
package org.sky.tree;
/**
*线索链表结点
*
* @author sky
*
* @param
*/
public class ThreadedBinaryTreeNode {
E element;
//左右孩子指针(引用)
ThreadedBinaryTreeNode leftChild;
ThreadedBinaryTreeNode rightChild;
//左右线索标识: true为线索,false为指针
boolean leftTag;
boolean rightTag;
public ThreadedBinaryTreeNode() {
super();
this.element = null;
this.leftChild = null;
this.rightChild = null;
this.leftTag = false; //默认值一定要设为false
this.rightTag = false; //默认值一定要设为false
}
public ThreadedBinaryTreeNode(E element) {
super();
this.element = element;
this.leftChild = null;
this.rightChild = null;
this.leftTag = false; //默认值一定要设为false
this.rightTag = false; //默认值一定要设为false
}
public ThreadedBinaryTreeNode(E element,
ThreadedBinaryTreeNode leftChild,
ThreadedBinaryTreeNode rightChild, boolean leftTag,
boolean rightTag) {
super();
this.element = element;
this.leftChild = leftChild;
this.rightChild = rightChild;
this.leftTag = leftTag;
this.rightTag = rightTag;
}
public E getElement() {
return element;
}
public void setElement(E element) {
this.element = element;
}
public ThreadedBinaryTreeNode getLeftChild() {
return leftChild;
}
public void setLeftChild(ThreadedBinaryTreeNode leftChild) {
this.leftChild = leftChild;
}
public ThreadedBinaryTreeNode getRightChild() {
return rightChild;
}
public void setRightChild(ThreadedBinaryTreeNode rightChild) {
this.rightChild = rightChild;
}
public boolean isLeftTag() {
return leftTag;
}
public void setLeftTag(boolean leftTag) {
this.leftTag = leftTag;
}
public boolean isRightTag() {
return rightTag;
}
public void setRightTag(boolean rightTag) {
this.rightTag = rightTag;
}
}
package org.sky.tree;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;
/**
* 二叉树的线索链表实现
*
* @author sky
*
* @param
*/
public class ThreadedBinaryTree<E> {
private ThreadedBinaryTreeNode root; //二叉链表根结点
private ThreadedBinaryTreeNode threadedRoot; //线索链表头结点
private ThreadedBinaryTreeNode preVisit = null; //线索化时用,用于指向刚刚访问过的结点
public ThreadedBinaryTree() {
super();
this.root = new ThreadedBinaryTreeNode();
}
public boolean isEmpty() {
if (root == null) {
return true;
}
return false;
}
public ThreadedBinaryTreeNode getThreadedRoot() {
return threadedRoot;
}
public ThreadedBinaryTreeNode getRoot() {
return root;
}
/**
* 将传入的数据随机分配在二叉树的结点,以生成二叉链表
* @param node
* @param element
*/
public void createTreeRandomly(ThreadedBinaryTreeNode node, E element){
if(root == null){
root = new ThreadedBinaryTreeNode(element);
}else{
if(Math.random() > 0.5){
if(node.leftChild == null){
node.leftChild = new ThreadedBinaryTreeNode(element);
}else{
createTreeRandomly(node.leftChild,element);
}
}else{
if(node.rightChild == null){
node.rightChild = new ThreadedBinaryTreeNode(element);
}else{
createTreeRandomly(node.rightChild,element);
}
}
}
}
/**
* 根据传入的集合创建完全二叉树
* 此处利用了完全二叉树父结点和子结点间的关系:如果i=1,则结点i为根结点,否则其双亲结点为[i/2];
* 如果2i > n,则结点i无左孩子,否则其左孩子结点为2i;
* 如果2i+1 > n,则结点i无右孩子,否则右孩子结点为2i+1;
* @param c
*/
public void createCompleteBinaryTree(Collection extends E> c){
if(c != null && c.size() > 0){
List> treeList = new LinkedList>();
for(Object o : c){
ThreadedBinaryTreeNode threadedBinaryTreeNode = new ThreadedBinaryTreeNode((E)o);
treeList.add(threadedBinaryTreeNode);
}
LinkedBlockingDeque> queue = new LinkedBlockingDeque>();
//对前treeList.size()/2 - 1个父节点按照父节点与孩子节点的数字关系建立二叉树
for(int parentIndex = 0; parentIndex < treeList.size()/2; parentIndex++){
if(parentIndex == 0){
root = treeList.get(parentIndex);
//左子树
root.leftChild = treeList.get(parentIndex*2 + 1);
queue.add(root.leftChild);
//右子树
root.rightChild = treeList.get(parentIndex*2 +2);
queue.add(root.rightChild);
}else{
if(!queue.isEmpty() && parentIndex*2+1 < treeList.size()){
ThreadedBinaryTreeNode node = (ThreadedBinaryTreeNode) queue.poll();
if(parentIndex*2+1 < treeList.size()){
//左子树
node.leftChild = treeList.get(parentIndex*2 + 1);
queue.add(node.leftChild);
}
if(parentIndex*2+2 < treeList.size()){
//右子树
node.rightChild = treeList.get(parentIndex*2 + 2);
queue.add(node.rightChild);
}
}else{
return ;
};
}
}
}
}
/**
* 中序遍历二叉树,并将其中序线索化
* 注:线索化的过程即为遍历的过程中修改空指针的过程
*/
public void inOrderThreading(ThreadedBinaryTreeNode root){
if(threadedRoot == null){
threadedRoot = new ThreadedBinaryTreeNode();
}
threadedRoot.leftTag = false;
threadedRoot.rightTag = true;
threadedRoot.rightChild = threadedRoot; //右指针回指
if(root == null){
threadedRoot.leftChild = threadedRoot;
}else{
threadedRoot.leftChild = root;
preVisit = threadedRoot;
inThreading(root); //中序线索化
preVisit.rightTag = true;
preVisit.rightChild = threadedRoot;
threadedRoot.rightChild = preVisit;
}
}
//中序遍历进行中序线索化
public void inThreading(ThreadedBinaryTreeNode currentVisit){
if(currentVisit != null){
inThreading(currentVisit.leftChild); //左子树线索化
if(currentVisit.leftChild == null){ //前驱线索
currentVisit.leftTag = true;
currentVisit.leftChild = preVisit;
}
if(preVisit.rightChild == null){ //后继线索
preVisit.rightTag = true;
preVisit.rightChild = currentVisit;
}
preVisit = currentVisit; //保持preVisit指向刚刚访问过的结点
inThreading(currentVisit.rightChild); //右子树线索化
}
}
/**
* 中序遍历线索二叉树
*/
public void inOrderTraverse(ThreadedBinaryTreeNode threadedRoot){
ThreadedBinaryTreeNode pointer = threadedRoot.leftChild; //指向根结点
while(pointer != threadedRoot){
while(pointer.leftTag == false){
pointer = pointer.leftChild;
}
visit(pointer); //访问左子树为空的结点
while(pointer.rightTag == true && pointer.rightChild != threadedRoot){
//访问后继结点
pointer = pointer.rightChild;
visit(pointer);
}
pointer = pointer.rightChild;
}
}
/**
* 访问当前结点
* @param current
*/
public void visit(ThreadedBinaryTreeNode current){
if(current != null && current.element != null){
System.out.println(current.element);
}else{
System.out.println("null");
}
}
}