(Java数据结构和算法)堆---优先队列、堆排序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ccnuacmhdu/article/details/84440631

堆主要用于实现优先队列。

利用有序数组可以实现优先队列(从小到大或从大到小的数组),删除的时间复杂度是O(1),但是插入的时间复杂度是O(N)。用堆实现优先队列,插入和删除的时间复杂度都是O(logN)。

简介

堆是一种完全二叉树,且每个节点的值都大于等于子节点值(大根堆)。堆的子树也是完全二叉树。注意是用数组来存放堆
在这里插入图片描述

补充下完全二叉树的规律

(1)n2+1 = n0

设子节点为0、1、2的节点数各是n0,n1,n2,总节点数是n,那么n = n0 + n1 + n2,同时对一棵树从上往下看,(假定不是空树)根节点有1个,依次往下走,有n = 1 + n00 + n11 + n2*2。上面两个等式可以推得:n2+1 = n0,证明得到子节点数是0的节点个数比子节点数是2的节点个数多1个。

(2)n = 2*n0 + n1 -1

这个式子从(1)的两个式子很容易推导出来。注意完全二叉树的子节点数是1的节点数目要么只有1个要么有0个,即n1的取值是0或1。这就可以把n1消掉,同时,还可以根据n的奇偶性,逼出n1取值到底是0还是1。

(3) 非叶子数是n/2(商)—在后面堆实现的时候trickleDown函数会用到

注意分析这个式子,当n是奇数的时候,n1必定是0,此时n0=(n+1)/2(叶子节点数),当n是偶数的时候,n1必定是1,此时n0=n/2。举个例子,比如n=8的时候,叶子节点数是n0=8/2=4,n=7的时候,叶子节点数是n0=(7+1)/2=4。这里有个规律是,无论n是奇数或偶数,不变的是非叶子节点数是n/2(商)。

移除

在这里插入图片描述

插入

在这里插入图片描述

堆实现

class Node{
	private int e;
	public Node(int e){
		this.e = e;
	}
	public int getE(){
		return this.e;
	}
	public void setE(int e){
		this.e = e;
	}
}

class Heap{
	private Node[] heapArray;
	private int maxSize;//容量
	private int size;//当前大小
	
	public Heap(int maxSize){
		this.maxSize = maxSize;
		size = 0;
		heapArray = new Node[maxSize];
	}
	public boolean isEmpty(){
		return this.size == 0;
	}
	
	public boolean insert(int e){
		if(size >= maxSize){
			return false;
		}
		Node newNode = new Node(e);
		heapArray[size] = newNode;
		trickleUp(size);
		size++;
		return true;
	}

	//插入新节点的时,先把新节点插入最后,再向上调整,下面是向上调整的函数
	public void trickleUp(int index){
		int parent = (index - 1)/2;
		Node bottom = heapArray[index];//保存一份新插入的节点
		while(index > 0 && bottom.getE() > heapArray[parent].getE()){
			heapArray[index] = heapArray[parent];//孩子比父亲大,把父亲给孩子,往下面拉,腾出位置
			index = parent;//继续往上找
			parent = (parent-1)/2;
		}
		heapArray[index] = bottom;
	}
	
	public Node remove(){
		if(0 == size){
			return null;
		}	
		Node root = heapArray[0];
		heapArray[0] = heapArray[--size];
		trickleDown(0);//下移
		return root;
	}

	public void trickleDown(int index){
		int largerChild;
		Node top = heapArray[index];
		while(index < size/2){//仍存在非叶子节点的时候,一个有n个节点的完全二叉树的非叶子节点树是size/2(商)
			int leftChild = 2*index + 1;
			int rightChild = leftChild + 1;
			//从左右两个孩子中找一个较大的孩子,注意有孩子未必存在,需要判定
			if(rightChild < size && heapArray[leftChild].getE() < heapArray[rightChild].getE() ){
				largerChild = rightChild;
			}else{
				largerChild = leftChild;
			}

			if(top.getE() >= heapArray[largerChild].getE()){
				break;
			}

			heapArray[index] = heapArray[largerChild];//把比父母大的孩子上移到父母处
			index = largerChild;
		}
		heapArray[index] = top;
	}

	public boolean change(int index, int newE){
		if(index < 0 || index >= size){
			return false;
		}
		int oldE = heapArray[index].getE();
		heapArray[index].setE(newE);
		if(newE > oldE){
			trickleUp(index);
		}else{
			trickleDown(index);
		}
		return true;
	}

	public void print(){
		for(int i = 0; i < size; i++){
			System.out.print(heapArray[i].getE()+" ");
		}
		System.out.println();
	}
}

public class Main {

	public static void main(String[] args){
		Heap heap = new Heap(10);
		int[] a = {4,1,3,6,9,7};
		for(int i = 0; i < a.length; i++){
			heap.insert(a[i]);
		}
		heap.print();
		System.out.println(heap.remove().getE()+" removed.");
		heap.print();
		heap.change(2, 66);
		heap.print();
	}
	
}

在这里插入图片描述

堆排序实现

思路:既然上述大顶堆最大的数永远在堆的顶部(树根),那就不断删除堆顶,直到树空,删除的这个序列就是从大到小排序的!

当然,使用大顶堆也可以实现从小到大排序,只需要把删除的序列逆序放在数组里就可以了。

猜你喜欢

转载自blog.csdn.net/ccnuacmhdu/article/details/84440631