【数据结构与算法06】堆

定义:用二叉树的思想实现的优先级队列,同时它是完全二叉树,即除最后一层以外,其他层都是满元素的。

运算限制:参考前面优先级队列。自动排序,同时使用二叉树的存储方式,但不使用二叉树的元素逻辑关系。

基本运算:

                      1,添加。

                      2:移除。

                      3:取最大值。

1,为了更好的理解,先来个简单的,还是用数组来实现,借助二叉树的思想,但是在这里我们把最大的元素永远放在下标0的位置。不再使用二叉树的中序排序。数组中取元素的父元素可以用公式【(index-1)/2】来得到,比如下标0是下标1和下标2的父元素,那么当我们新添加一个元素的时候,直接把这个元素放到数组对应的下标位置3,然后让它与自己的父元素1比较,如果3位置元素大于1位置元素,就把3位置元素的值更新为1位置元素的值,循环上面的比较直到新元素不在大于父元素,把新元素放在最后一个小于自己的父元素位置。完成这个过程保证了父元素永远是大于子元素。

既然我们已经把最大的元素放到了下标0的位置,那我们肯定就是取最大值啦。同时如果我们是移除最大值,那我们需要选出新的最大值。首先拿到最大值,也就是下标0的元素,然后用最后一个元素来填充下标0,再让它与自己的较大子元素比较,如果它比子元素都要大,那它就是最大的,正是我们想要的,但那是不可能的,因为要是它是最大的就不会是最后一个元素了。既然它比较大子元素小,那就让它等于较大子元素,循环上面的过程,直到大于子元素,把它放在最后一个较大子元素的位置。取左子元素的下标公式为【2*index+1】,所以上面的过程必须在下标小于数组长度一半的情况下执行,不然2*index直接大于数组长度了,哪里来的子元素呢。

下面看代码吧

public class ArrayHeap {
	private Node[] array;
	private int size;
	private int nItems;
	
	public ArrayHeap(int initSize){
		this.size = initSize;
		this.array = new Node[size];
		nItems = 0 ;
	}
	
	public void insert(int value){
		if(size==nItems) throw new ArrayIndexOutOfBoundsException(size);
		Node node = new Node();
		node.data = value;
		array[nItems] = node;
		up(nItems++);
	}

	 /**
	 * @param i
	 */
	private void up(int index) {
		int parent = (index-1)/2 ;
		Node botton = array[index] ;
		while (index>0 && array[parent].data<botton.data) {
			array[index] = array[parent] ;
			index = parent;
			parent = (parent-1)/2;
		}
		array[index] = botton;
	}

	public int remove() {
		Node root = array[0];
		array[0] = array[--nItems];
		down(0);
		return root.data;
	}
	/**
	 * @param i
	 */
	private void down(int index) {
		int largeChild ;
		Node top = array[index] ;
		while(index<nItems/2){
			int left = 2*index+1;
			int right = left +1;
			if(right<nItems && array[left].data<array[right].data){
				largeChild = right;
			}else{
				largeChild = left;
			}
			if(top.data>=array[largeChild].data){
				break;
			}
			array[index] = array[largeChild] ;
			index = largeChild;	
		}
		array[index] = top ;
	}
	
	public Integer max(){
		if(nItems == 0) throw new NullPointerException();
		return array[0].data;
	}
	
	static class Node{
		int data;
	}
	
	
	public static void main(String[] args) {
		ArrayHeap arrayHeap = new ArrayHeap(10);
		arrayHeap.insert(10);
		arrayHeap.insert(20);
		arrayHeap.insert(5);
		arrayHeap.insert(30);
		while(arrayHeap.nItems>0){
			System.out.println("while-min"+arrayHeap.max());
			System.out.println("while-remove"+arrayHeap.remove());
		}
		System.out.println("out-while-min"+arrayHeap.max());
	}
}

运行结果

while-min30
while-remove30
while-min20
while-remove20
while-min10
while-remove10
while-min5
while-remove5
Exception in thread "main" java.lang.NullPointerException
	at data.queue.ArrayHeap.max(ArrayHeap.java:77)
	at data.queue.ArrayHeap.main(ArrayHeap.java:96)

2,下面是用真正二叉树实现部分。

其实过程是一样的,只不过下面要使用二叉树实现,但是在二叉树中添加时找新节点的位置和移除时找最后一个节点并不想数组中操作那么简单,当然了这里自然也不会像二叉树那样处理。看下面。。

假如堆原本有3个节点,新添加一个,节点个数变成4,这个节点应该放在哪,前面说过堆是完全二叉树,根节点的左子节点的左子节点,再添加一个节点数为5,放在根节点的左子节点的右子节点。4的二进制100,而5的二进制是101,发现第二个数都是0巧的是这两个节点都是在根节点的左子节点下面,下面看第三个数,4是0我们把它放在了左子节点,5是1我们把它放在了右子节点。0就是左子,1就是右子。按照这个规律,再新添加节点,我们拿到节点个数转成二进制就能知道把它放在哪,6为110,根的右的左。现在知道怎么找添加时新节点的位置和移除时最后一个节点了吧。

看代码吧

public class Heap {
	private Node top ; //顶部节点
	private int nItems = 0; //节点数
	
	public int getnItems() {
		return nItems;
	}
	public void insert(int value){
		//找到新节点的位置
		if(top == null){
			top = new Node();
			top.data = value;
			nItems ++ ;
		}else{
			Node newNode = addNode();
			newNode.data = value ;
			nItems ++ ;
			//大的元素上移
			ticketUp(newNode);
		}
	}
	
	public int remove(){
		if(top == null) throw new NullPointerException();
		int value = top.data;
		Node node = getLastNode();
		if(node == top){
			top = null;
			nItems--;
		}else{
			top.data=node.data;
			node = null;
			nItems--;
			ticketDown(top);
		}
		return value;
	}
	
	private void ticketUp(Node node){
		int exchange = node.data ;
		if(node!=null && node.p !=null && node.data>node.p.data){
			node.data = node.p.data ;
			node.p.data = exchange;
			ticketUp(node.p);
		}
	}
	
	private void ticketDown(Node node){
		int exchange = node.data ;
		Node large ;
		if(node!=null && node.l!=null && node.r!=null ){
			large = node.l.data>node.r.data?node.l:node.r;
			node.data = large.data;
			large.data = exchange;
			ticketDown(large);
		}
	}
	
	private Node addNode(){
		Node newNode = top ;
		Node parentNode = top ;
		String binarySystem = Integer.toBinaryString(nItems+1);
		for(int i =1;i<binarySystem.length();i++){
			parentNode = newNode ;
			if(binarySystem.charAt(i)=='0'){
				newNode = newNode.l;
			}else{
				newNode = newNode.r;
			}
		}
		newNode = new Node();
		newNode.p = parentNode ;
		if(parentNode.l ==null){
			parentNode.l=newNode ;
		}else{
			parentNode.r=newNode ;
		}
		return newNode ;
	}
	
	private Node getLastNode(){
		Node node = top ;
		String binarySystem = Integer.toBinaryString(nItems);
		for(int i =1;i<binarySystem.length();i++){
			if(binarySystem.charAt(i)=='0'){
				node = node.l;
			}else{
				node = node.r;
			}
		}
		return node ;
	}
	
	public int max(){
		if(top == null) throw new NullPointerException();
		return top.data;
	}
	
	
	static class Node{
		int data ;//节点数据
		Node p ;//父节点
		Node l ;//左子节点
		Node r ;//右子节点
	}

	
	public static void main(String[] args) {
		Heap heap = new Heap();
		heap.insert(10);
		heap.insert(20);
		heap.insert(15);
		heap.insert(60);
		heap.insert(5);
		while (heap.getnItems()>0) {
			System.out.println("while-max:"+heap.max());
			System.out.println("while-remove:"+heap.remove());
		}
		System.out.println("while-out-max:"+heap.max());
	}
}

运行结果

while-max:60
while-remove:60
while-max:20
while-remove:20
while-max:15
while-remove:15
while-max:10
while-remove:10
while-max:5
while-remove:5
Exception in thread "main" java.lang.NullPointerException
	at data.queue.Heap.max(Heap.java:108)
	at data.queue.Heap.main(Heap.java:132)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值