二叉树之线索链表
2016-12-02 18:21
453 查看
上一章介绍了二叉树的二叉链表的实现,并给出了相关遍历算法,但是,当以二叉链表作为二叉树的存储结构时,无法直接得到结点在任一序列中的前驱与后继信息。为了规避这个弊端,本章将引入线索二叉树的概念 ,并给出相关Java实现。
为了得到前驱与后继的信息,最直观的想法是在每个结点上添加两个指针域,分别指向结点在遍历时的前驱及后继。然而,这样会使存储密度大大降低,而在有n个结点的二叉树中有n+1个空链域,故可利用这些空链域来存放结点的前驱及后继信息,并作如下规定:若结点有左子树,则其leftChild域指向其左孩子,否则,令leftChild指向其前驱;若结点有右子树,则其rightChild域指向其右孩子,否则,令rightChild指向其后继。
为了避免混淆,现对上章中的结点结构作适当改动,改动后如下:
并规定:leftTag取值为false时,指示结点的左孩子,取值为true时,指示结点的前驱;rightTag取值为false时,指示结点的右孩子,取值为true时,指示结点的后继。
线索链表:以上述结点结构构成的二叉链表作为二叉树的存储结构,其中指示结点前驱及后继信息的指针称作线索,加上线索的二叉树称为线索二叉树。
线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程。
线索化的实质:修改空指针(将二叉链表中的空指针改为指向前驱或后继的线索)。
下面给出相关java代码实现:
为了得到前驱与后继的信息,最直观的想法是在每个结点上添加两个指针域,分别指向结点在遍历时的前驱及后继。然而,这样会使存储密度大大降低,而在有n个结点的二叉树中有n+1个空链域,故可利用这些空链域来存放结点的前驱及后继信息,并作如下规定:若结点有左子树,则其leftChild域指向其左孩子,否则,令leftChild指向其前驱;若结点有右子树,则其rightChild域指向其右孩子,否则,令rightChild指向其后继。
为了避免混淆,现对上章中的结点结构作适当改动,改动后如下:
leftChilt | leftTag | element | rightTag | rightChild |
---|
线索链表:以上述结点结构构成的二叉链表作为二叉树的存储结构,其中指示结点前驱及后继信息的指针称作线索,加上线索的二叉树称为线索二叉树。
线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程。
线索化的实质:修改空指针(将二叉链表中的空指针改为指向前驱或后继的线索)。
下面给出相关java代码实现:
1. 二叉链表结点
package org.sky.tree; /** *线索链表结点 * * @author sky * * @param <E> */ public class ThreadedBinaryTreeNode<E> { E element; //左右孩子指针(引用) ThreadedBinaryTreeNode<E> leftChild; ThreadedBinaryTreeNode<E> 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<E> leftChild, ThreadedBinaryTreeNode<E> 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<E> getLeftChild() { return leftChild; } public void setLeftChild(ThreadedBinaryTreeNode<E> leftChild) { this.leftChild = leftChild; } public ThreadedBinaryTreeNode<E> getRightChild() { return rightChild; } public void setRightChild(ThreadedBinaryTreeNode<E> 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; } }
2. 线索链表实现
package org.sky.tree; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.concurrent.LinkedBlockingDeque; /** * 二叉树的线索链表实现 * * @author sky * * @param <E> */ public class ThreadedBinaryTree<E> { private ThreadedBinaryTreeNode<E> root; //二叉链表根结点 private ThreadedBinaryTreeNode<E> threadedRoot; //线索链表头结点 private ThreadedBinaryTreeNode<E> preVisit = null; //线索化时用,用于指向刚刚访问过的结点 public ThreadedBinaryTree() { super(); this.root = new ThreadedBinaryTreeNode<E>(); } public boolean isEmpty() { if (root == null) { return true; } return false; } public ThreadedBinaryTreeNode<E> getThreadedRoot() { return threadedRoot; } public ThreadedBinaryTreeNode<E> getRoot() { return root; } /** * 将传入的数据随机分配在二叉树的结点,以生成二叉链表 * @param node * @param element */ public void createTreeRandomly(ThreadedBinaryTreeNode<E> node, E element){ if(root == null){ root = new ThreadedBinaryTreeNode<E>(element); }else{ if(Math.random() > 0.5){ if(node.leftChild == null){ node.leftChild = new ThreadedBinaryTreeNode<E>(element); }else{ createTreeRandomly(node.leftChild,element); } }else{ if(node.rightChild == null){ node.rightChild = new ThreadedB cd8b inaryTreeNode<E>(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<ThreadedBinaryTreeNode<E>> treeList = new LinkedList<ThreadedBinaryTreeNode<E>>(); for(Object o : c){ ThreadedBinaryTreeNode<E> threadedBinaryTreeNode = new ThreadedBinaryTreeNode<E>((E)o); treeList.add(threadedBinaryTreeNode); } LinkedBlockingDeque<ThreadedBinaryTreeNode<E>> queue = new LinkedBlockingDeque<ThreadedBinaryTreeNode<E>>(); //对前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<E> node = (ThreadedBinaryTreeNode<E>) 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<E> root){ if(threadedRoot == null){ threadedRoot = new ThreadedBinaryTreeNode<E>(); } 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<E> 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<E> threadedRoot){ ThreadedBinaryTreeNode<E> 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<E> current){ if(current != null && current.element != null){ System.out.println(current.element); }else{ System.out.println("null"); } } }
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- Debian 7.x 安装Oracle JAVA
- springmvc实现url路由功能
- spring boot 配置 druid/** * 配置druid * Created by adam on 4/11/16. */ @Configuration public class D
- api接口rsa加密
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解