Java基础 - 堆树(Heap)

package com.yc.tree;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

/**
 * @author wb
 * @param <T>
 * 
 * 
 * 堆(Heap)的定义为:堆是一棵二叉树,而且根的关键字大于子树的关键字。不管左子树和右子树的大小顺序,这是与二叉查找树最大的差异。如图7-1所示。
 * 					30
 * 					││
 * 				25──┘└──────22
 * 				││		 	││					
 * 		   19───┘└──13  18──┘└──10
 * 		   ││		│
 * 	   15──┘└─14  11┘
 * 					图7-1
 * 它是一个堆,而不是二叉查找树。为了方便,下面以Heap表示。
 * Heap可用在排序上,简称为Heap Sort,在一堆杂乱无章的数据中,利用Heap Sort进行由小至大或由大至小的排序。
 * 首先将一堆数据利用完全二叉树将其建立起来,在将它调整为Heap,可以用栈(由小至大)或者队列(由大至小)来辅助完成。
 * 
 * 在调整过程中有两种方式,一是由上而下,跟根节点开始与子节点比较,若前者大则交换,反之,则不交换,以符合父节点大于子节点为准。
 * 另一种方法就是先让子节点比较,找出较大者在于其父节点比较,这种方法最多做一次交换。
 * 
 * 第二种方式是由下而上,选中的节点与其父节点比较,只要大于父节点的关键字则交换他们的位置,在以交换后父节点为选中的节点递归向上比较。
 * 
 * Heap中增加节点:
 * 首先按照完全二叉树的特点将新节点加进来,然后对新的Heap进行维护,使它仍满足Heap的性质。
 * 
 * Heap中删除节点:
 * Heap的删除是将完全二叉树的最后一个节点取代被删除的节点,然后判断是否为一个Heap,如不是,则进行调整。
 * 
 * 说到这里,关于堆树有个很重要的东西,就是他的最后一个节点,因为节点的增加和删除都和它有关。下面给出堆树的最后一个节点的求法:
 * 因为堆树是一棵完全二叉树,结合前面的哈夫曼树,同样的我们把树的左拐记做0,右拐记做1,则有如下图7-2所示完全二叉树。
 * 
 * 					   1 1
 * 				      0	││1
 * 			     2──────┘└───────3 
 * 			   0││1		        0││1
 * 			4───┘└─5         6───┘└────7
 * 		   0││1	  0││1
 * 		  8─┘└─9 10┘└11 
 * 				图7-2
 * 拿上图的第 9个节点来说,从根节点1出发,左拐经过2,在左拐经过4,最后右拐到达9,此时可以用二进制1001来表示完全二叉树的第9个节点。
 * 发现了这一点我们就可以求出堆树的最后一个节点了。首先我们有一个记录节点数目的变量nodeCount,如果要得到该堆树的最后一个节点,只需将
 * nodeCount转换成二进制字符串,然后从根节点开始,逢0左拐,逢1右拐,最后得到该树的最后一个节点。
 * 
 * 
 * 下面的示例代码演示的是大顶堆(Max Heap),通过大顶堆我们可以知道一组数据的最大值在堆顶。相反的还有一种小顶堆(Min Heap),
 * 通过小顶堆我们可以知道一组数据的最小值在堆顶。
 * 除了上面的大顶堆和小顶堆之外,还有一种数据结构Deap。
 * Deap同样也具备Max Heap与Min Heap的特征,其定义如下:
 * 1.Deap的根不存储任何数据,为空节点
 * 2.根的左子树是Min Heap,右子树是Max Heap
 * 3.Min Heap与Max Heap存在一一对应的关系,假设左子树中有一节点i,则在右子树中必存在一节点j与i对应,则i必须小于j。
 * 	换成数学模式来说就是,在根节点的左子树中有10xxxx位置的节点与根节点的右子树中11xxxx位置的节点对应且小于他。但如果右子树
 * 	中11xxxx位置的节点不存在,那么换成11xxxx位置节点的父节点与之对应。
 * 经上面的介绍可知Deap可以知道一组数据的最大值和最小值。
 * 
 */
public class HeapTree <T extends Comparable<T>>{
	public class Node{
		T data;
		Node parent;
		Node left;
		Node right;
		
		public Node(){
		}
		public Node(T data, Node parent, Node left, Node right){
			this.data = data;
			this.parent = parent;
			this.left = left;
			this.right = right;
		}
		
		@Override
		public String toString(){
			return "[data=" + data + "]";
		}
		public boolean equales(Node obj){
			if(this == obj){
				return true;
			}
			if(getClass() == obj.getClass()){
				Node tmp = (Node)obj;
				return data.equals(tmp.data) &&
						parent == tmp.parent &&
						left == tmp.left &&
						right == tmp.right;
			}
			return false;
		}
	}
	//根节点
	private Node root;
	//节点的数目
	private int nodeCount;
	public HeapTree(){
		root = new Node();
		nodeCount ++;
	}
	public HeapTree(T data){
		root = new Node(data, null, null, null);
		nodeCount ++;
	}
	
	/**
	 * 给指定的数组建大顶堆并返回根节点
	 * @param arr:指定数组
	 */
	public Node buildMaxHead(T[] arr){
		if(arr != null && arr.length > 0){
			root = new Node(arr[0], null, null, null);
			
			Node tmp = null;
			for(int i = 1; i < arr.length; i ++){
				tmp = addForArr(arr[i], i + 1);
				fixForUp(tmp);
			}
			
			nodeCount = arr.length;
		}
		return root;
	}
	
	/**
	 * 添加节点并返回新添加的节点
	 * @param data
	 */
	private Node addForArr(T data, int index) {
		if(root == null){
			root = new Node(data, null, null, null);
			nodeCount ++;
			return root;
		}
		else{
			Node current = root;
			Node parent = current;
			//将十进制转换成2进制字符串
			String binLoc = Integer.toBinaryString(index);
			for(int i = 1; i < binLoc.length(); i ++){
				parent = current;
				
				char c = binLoc.charAt(i);
				switch (c) {
				case '0':
					current = current.left;
					break;
				case '1':
					current = current.right;
					break;
				default:
					break;
				}
			}
			//以上代码是找出新节点的父节点parent
			if(parent.left == null){
				parent.left = new Node(data, parent, null, null);
				nodeCount ++;
				return parent.left;
			}else{
				parent.right = new Node(data, parent, null, null);
				nodeCount ++;
				return parent.right;
			}
		}
	}
	//添加节点
	public void add(T data){
		Node node = addForArr(data, nodeCount  + 1);
		fixForUp(node);
	}
	
	/**
	 * 删除指定元素的节点
	 * @param data
	 * @return
	 */
	public void remove(T data){
		Node delNode = getNode(data);
		Node lastNode = lastNode();
		
		if(delNode == null){
			return;
		}else{
			//如果是叶子节点
			if(delNode.left == null && delNode.right == null){
				if(root == delNode){
					root = null;
				}else{
					if(lastNode == lastNode.parent.left){
						delNode.data = lastNode.data;
						lastNode.parent.left = null;
						lastNode.parent = null;
						fixForUp(delNode);
						
						nodeCount --;
					}else{
						delNode.data = lastNode.data;
						lastNode.parent.right = null;
						lastNode.parent = null;
						fixForUp(delNode);
						
						nodeCount --;
					}
				}
			}
			//只有左子节点(左子树(因为是完全二叉树))(没有只有右子节点的情况)
			else if(delNode.left != null && delNode.right == null){
				delNode.data = delNode.left.data;
				//delNode.data = lastNode.data;
				delNode.left.parent = null;
				delNode.left = null;
				nodeCount --;
			}
			//有两个子树
			else{
				delNode.data = lastNode.data;
				fixForDown(delNode);
				
				if(lastNode == lastNode.parent.left){
					lastNode.parent.left = null;
					lastNode.parent = null;
				}else{
					lastNode.parent.right = null;
					lastNode.parent = null;
				}
				
				nodeCount --;
			}
		}
	}
	//获取指定元素的节点
	public Node getNode(T data){
		Deque<Node> deque = new ArrayDeque<Node>();
		if(root != null){
			deque.offer(root);
		}
		while(!deque.isEmpty()){
			Node tmp = deque.poll();
			int comp = data.compareTo(tmp.data);
			if(comp == 0){
				return tmp;
			}
			if(tmp.left != null){
				deque.offer(tmp.left);
			}
			if(tmp.right != null){
				deque.offer(tmp.right);
			}
		}
		return null;
	}
	//获取堆树的最后一个节点
	private Node lastNode(){
		if(nodeCount == 0){
			return null;
		}else if(nodeCount == 1){
			return root;
		}else{
			Node current = root;
			String binCount = Integer.toBinaryString(nodeCount);
			for(int i = 1; i < binCount.length(); i ++){
				char c = binCount.charAt(i);
				switch (c) {
				case '0':
					current = current.left;
					break;
				case '1':
					current = current.right;
					break;
				default:
					break;
				}
			}
			return current;
		}
	}
	/**
	 * 向上对堆树的维护
	 * @param node
	 */
	private void fixForUp(Node node) {
		if(root == node){
			return;
		}else{
			int tmpComp = node.data.compareTo(node.parent.data);
			T tmpData;
			
			if(tmpComp > 0){ //如果新添加的子节点大于它的父节点,则交换他们的data
				tmpData = node.parent.data ;
				node.parent.data = node.data;
				node.data = tmpData;
			}
			fixForUp(node.parent);
		}
	}
	/**
	 * 向下对堆树的维护(仅限删除含有两支的节点)
	 * @param node
	 */
	private void fixForDown(Node node) {
		if(node.left != null && node.right != null){
			int comp = node.left.data.compareTo(node.right.data);
			if(comp > 0){//左边大
				comp = node.left.data.compareTo(node.data);
				if(comp > 0){//还是左边大
					T tmpData = node.data;
					node.data = node.left.data;
					node.left.data = tmpData;
					//以node的左子节点递归
					fixForDown(node.left);
				}
			}else{//右边大
				comp = node.right.data.compareTo(node.data);
				if(comp > 0){//还是右边大
					T tmpData = node.data;
					node.data = node.right.data;
					node.right.data = tmpData;
					//以node的左子节点递归
					fixForDown(node.right);
				}
			}
		}
	}
	
	//广度优先遍历
	public List<Node> breadthFirstSearch(){
		return cBreadthFirstSearch(root);
	}
	private List<Node> cBreadthFirstSearch(Node node) {
		List<Node> nodes = new ArrayList<Node>();
		Deque<Node> deque = new ArrayDeque<Node>();
		
		if(node != null){
			deque.offer(node);
		}
		while(!deque.isEmpty()){
			Node head = deque.poll();
			nodes.add(head);
			if(head.left != null){
				deque.offer(head.left);
			}
			if(head.right != null){
				deque.offer(head.right);
			}
		}
		return nodes;
	}
	
	public static void main(String[] args) {
		HeapTree<Integer> tree = new HeapTree<Integer>();
		Integer[] intData = {9, 12, 3, 8, 7, 4, 2};
		tree.buildMaxHead(intData);
		tree.add(13);
		System.out.println( tree.breadthFirstSearch());
		
		tree.remove(12);
		System.out.println( tree.breadthFirstSearch());

	}
}

测试结果如下:

[[data=13], [data=12], [data=4], [data=9], [data=7], [data=3], [data=2], [data=8]]
[[data=13], [data=9], [data=4], [data=8], [data=7], [data=3], [data=2]]


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值