现在的位置: 首页 > 综合 > 正文

数据结构–线索二叉树

2014年09月28日 ⁄ 综合 ⁄ 共 5600字 ⁄ 字号 评论关闭

我们先来看一张之前整理过的一张二叉树的链式存储结构



1、每个数据域,都有2个指针域,分别指向该节点的左孩子、右孩子,但是每个节点前驱、后继,要知道的话需要遍历整棵树,这在时间上耗费很大。

2、另外,在叶子节点中,我们可以看到如图,每个节点都会浪费2块存储空间,N个节点的二叉树,2N个指针域,连接线为2N-1,那么会有2N-(N-1) = N+1个指针域浪费掉。

为了优化以上2个问题,我们引入了线索二叉树。


我们将二叉树中序遍历后。

我们把所有空指针域的右孩子,指向它的后继节点。如图


空指针域的左孩子,指向它的前驱。如图


我们把这种指向前序和后继的指针称为线索,加上线索的链表称为线索链表,相应的二叉树称为线索二叉树。


这样我们的指针域就都被使用上了,但是还存在另外一个问题,我们怎么区分左孩子、右孩子、前驱、后继,这些指针域呢?

所以这里我们要改下存储结构,对每个指针域增加一个标示ltag、rtag,如图



存储结构转化,0代表左孩子、右孩子指针域,1代表前驱、后继指针域,如图。



源码来自:http://www.cnblogs.com/ttltry-air/archive/2012/07/10/2584312.html

package com.test;

class TBTreeNode {
	int data;
	int LTag; // 0,1
	int RTag; // 0,1
	TBTreeNode lchild;
	TBTreeNode rchild;

	public TBTreeNode(int data) {
		this(data, null, null, 0, 0);
	}

	public TBTreeNode(int data, TBTreeNode lchild, TBTreeNode rchild, int LTag,
			int RTag) {
		this.data = data;
		this.lchild = lchild;
		this.rchild = rchild;
		this.LTag = LTag;
		this.RTag = RTag;
	}
}

class ThreadedBinaryTree {
	TBTreeNode head;
	TBTreeNode root;

	public void initTBTree() {
		head = new TBTreeNode(-1);
	}

	public void buildTBTree(int[] data) {
		head = null;
		root = new TBTreeNode(data[0]);
		for (int i = 1; i < data.length; i++) {
			TBTreeNode tmpNode = root;
			while (true) {
				if (tmpNode.data == data[i])
					break;
				// 小于等于根节点
				if (tmpNode.data > data[i]) {
					// 如果左孩子为空,这把当前数组元素插入到左孩子节点的位置
					if (tmpNode.lchild == null) {
						tmpNode.lchild = new TBTreeNode(data[i]);
						break;
					}
					// 如果不为空的话,则把左孩子节点用来和当前数组元素作比较
					tmpNode = tmpNode.lchild;
				} else // 大于根节点
				{
					// 如果右孩子为空,这把当前数组元素插入到左孩子节点的位置
					if (tmpNode.rchild == null) {
						tmpNode.rchild = new TBTreeNode(data[i]);
						break;
					}
					// 如果不为空的话,则把右孩子节点用来和当前数组元素作比较
					tmpNode = tmpNode.rchild;
				}
			}
		}
	}

	// 中序遍历二叉树,并将其中序线索化
	public void inOrderThreading() {
		TBTreeNode current;
		TBTreeNode previous;

		initTBTree();// head节点的初始化,root节点为用户创建的二叉树

		head.LTag = 0;
		head.RTag = 1;
		// 二叉树为空的时候,头结点指向其本身
		if (root == null) {
			head.lchild = head.rchild = head;
		} else {
			current = root;

			head.lchild = current;
			previous = head;
			previous = inThreading(current, previous);
			System.out.println("建立线索二叉树后,previous指针的值为:" + previous.data);
			previous.RTag = 1;
			previous.rchild = head;
			head.rchild = previous;
			System.out.println("建立线索二叉树后,最后一个节点为:" + previous.data
					+ ",对应的后继节点为:" + previous.rchild.data);
		}
	}

	// 前驱后继都是相对于头结点和叶子节点而言
	// 其中current指针指向当前访问的节点;previous节点指向刚刚访问过的节点
	private TBTreeNode inThreading(TBTreeNode current, TBTreeNode previous) {
		if (current != null) {
			TBTreeNode tmpNode = inThreading(current.lchild, previous);
			// 前驱线索
			if (current.lchild == null && current.LTag == 0) {
				current.LTag = 1;
				current.lchild = previous;
			}
			previous = tmpNode;
			// 后继线索
			if (previous.rchild == null && previous.RTag == 0) {
				previous.RTag = 1;
				previous.rchild = current;
			}

			previous = current;// 保持previous指向current的前驱
			previous = inThreading(current.rchild, previous);

			return previous;
		}
		return previous;
	}

	// 查找二叉查找树的最小节点:线索化二叉树前后的区别
	public TBTreeNode getFirstTBTNode(TBTreeNode node) {
		if (head != null) {
			while (node.lchild != head) {
				node = node.lchild;
			}
		} else {
			while (node.lchild != null) {
				node = node.lchild;
			}
		}
		return node;
	}

	// 查找二叉查找树的最大节点
	public TBTreeNode getLastTBTNode(TBTreeNode node) {
		if (head == null) {
			while (node.rchild != null) {
				node = node.rchild;
			}
		} else {
			while (node.rchild != head) {
				node = node.rchild;
			}
		}
		return node;
	}

	// 查找节点的前驱节点
	public TBTreeNode getPredecessor(TBTreeNode node) {
		if (node.lchild != null) {
			return getLastTBTNode(node.lchild);// 左子树的最大值
		}
		TBTreeNode parent = getParent(node);
		while (parent != null && node == parent.lchild) {// 向上找到最近的一个节点,其父亲节点的右子树包涵了当前节点或者其父亲节点为空
			node = parent;
			parent = getParent(parent);
		}
		return parent;
	}

	// 查找节点的后继节点
	public TBTreeNode getSuccessor(TBTreeNode node) {
		if (node.rchild != null) {
			return getFirstTBTNode(node.rchild);// 右子树的最小值
		}
		TBTreeNode parent = getParent(node);
		if (parent == null)
			return null;
		while (parent != null) {
			if (parent.lchild == node) {
				return parent; // 为左子树情况,后继为父节点
			} else {
				node = parent; // 否则递归
				parent = getParent(parent);
			}
		}
		return parent;
	}

	// 求出父亲节点,在定义节点类BSTreeNode的时候,没有申明父亲节点,所以这里专门用parent用来输出父亲节点(主要是不想修改代码了,就在这里加一个parent函数吧)
	public TBTreeNode getParent(TBTreeNode node) {
		TBTreeNode p = root;
		TBTreeNode tmp = null;
		while (p != null && p.data != node.data) {// 最后的p为p.data等于k.data的节点,tmp为p的父亲节点
			if (p.data > node.data) {
				tmp = p;// 临时存放父亲节点
				p = p.lchild;
			} else {
				tmp = p;// 临时存放父亲节点
				p = p.rchild;
			}
		}
		return tmp;
	}

	/**
	 * 线索化的递归遍历二叉树
	 */
	public void inOrderReaversal() {
		TBTreeNode node;
		if (head != null) {
			node = head.lchild; // node表示head头指针指向的root节点
			// 空树或者遍历结束 node==head
			while (node != head) {
				// 访问左子树
				while (node.LTag == 0)
					node = node.lchild;
				System.out.print(node.data + "   ");
				while (node.RTag == 1 && node.rchild != head) {
					// 访问叶子节点的后继
					node = node.rchild;
					System.out.print(node.data + "   ");
				}
				// 访问完叶子节点的后继后,访问右子树
				node = node.rchild;
			}
		}
	}

	/**
	 * 未线索化的中序递归遍历二叉树
	 */
	public void traversalTBTree() {
		traversalTBTree(root);
		System.out.println();
	}

	private void traversalTBTree(TBTreeNode node) {
		if (node != null) {
			traversalTBTree(node.lchild);
			System.out.print(node.data + "  ");
			traversalTBTree(node.rchild);
		}
	}
}

public class ThreadedBinaryTreeTest {
	public static void main(String[] args) {
		ThreadedBinaryTree tbTree = new ThreadedBinaryTree();
		/***********************************************************************
		 * 初始化操作
		 **********************************************************************/
		int[] data = { 2, 8, 7, 4, 9, 3, 1, 6, 7, 5 }; // { 8, 7, 1, 6, 4, 5,
		// 10, 3, 2, 9 };
		tbTree.buildTBTree(data);
		System.out.println("########################################");
		System.out.println("未进行线索化前,二叉树中序遍历结果:");
		tbTree.traversalTBTree();
		System.out.println(tbTree.head == null);
		System.out.println("未进行线索化前,二叉树中第一个节点和最后一个节点值分别为:"
				+ tbTree.getFirstTBTNode(tbTree.root).data + "   "
				+ tbTree.getLastTBTNode(tbTree.root).data);

		/***********************************************************************
		 * 中序线索化操作
		 **********************************************************************/
		System.out.println("########################################");
		System.out.println("线索化后,二叉树遍历结果:");
		tbTree.inOrderThreading();
		tbTree.inOrderReaversal();
		System.out.println();
		System.out.println("线索化后,head头指针的左子节点和后继节点分别为:"
				+ tbTree.head.lchild.data + "   " + tbTree.head.rchild.data);
		System.out.println("线索化后,二叉树中第一个节点和最后一个节点值分别为:"
				+ tbTree.getFirstTBTNode(tbTree.root).data + "   "
				+ tbTree.getLastTBTNode(tbTree.root).data);

	}
}


抱歉!评论已关闭.