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]]