目录
1 概念
2 堆的概念
2.1小根堆
2.2大根堆
3堆的存储方式
4、堆的创建
4.1堆向下调整
5、时间复杂度
6、堆的插入(向上调整)
7、堆的删除
8、PriorityQueue的特性
9、堆排序
1 概念
我们知道的队列,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队 列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话;初中那会班主任排座位时可能会让成绩好的同学先挑座位。
JDK1.8 中的 PriorityQueue 底层使用了堆这种数据结构,而堆实际就是在 完全二叉树 的基础上进行了一些调整。
2 堆的概念
2.1小根堆
2.2大根堆
3堆的存储方式
从堆的概念可知, 堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储
4、堆的创建
4.1堆向下调整
对于集合 { 27,15,19,18,28,34,65,49,25,37 } 中的数据,如果将其创建成堆呢?
向下过程 ( 以小堆为例 ) :
1. 让 parent 标记需要调整的节点, child 标记 parent 的左孩子 ( 注意: parent 如果有孩子一定先是有左孩子 )
2. 如果 parent 的左孩子存在,即 :child < size , 进行以下操作,直到 parent 的左孩子不存在
parent 右孩子是否存在,存在找到左右孩子中最小的孩子,让 child 进行标
将 parent 与较小的孩子 child 比较,如果: parent小于较小的孩子 child ,调整结束 否则:交换parent 与较小的孩子 child ,交换完成之后, parent 中大的元素向下移动,可能导致子树不满足对的性质,因需要继续向下调整,即parent = child ; child = parent*2+1; 然后继续步骤 2 。
//创建堆
public void creatHeap(){int preheap;//每次的根节点for ( preheap=(usesize-1-1)/2;preheap>=0;preheap--){signHeap(preheap,usesize);}}//向下调整为大根堆为例private void signHeap(int preheap,int end){//向下调整int child=preheap*2+1;//preheap左子树下标while (child<end){if(child+1<end&&elem[child]<elem[child+1])child++;//现在child指向孩子节点的最大值if (elem[preheap]<elem[child]){swap(preheap,child);preheap=child;child=child*2+1;}else break;}}private void swap(int i,int j) {int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}
5、时间复杂度
因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明 ( 时间复杂度本来看的就是近似值,多几个节点不影响最终结果) :
6、堆的插入(向上调整)
堆的插入总共需要两个步骤:
1. 先将元素放入到底层空间中(注意:空间不够时需要扩容)
2. 将最后新插入的节点向上调整,直到满足堆的性质
public void offer(int key){if(isFull()) Arrays.copyOf(elem,elem.length*2);//扩容elem[usesize]=key;usesize++;//调整为大根堆,向上调整siftUp(usesize-1);}//向上调整private void siftUp(int child) {int parent = (child-1)/2;while (child > 0) {//或者parent>0if(elem[child] > elem[parent]) {swap(child,parent);child = parent;parent = (child-1)/2;}else {break;}}}
7、堆的删除
1. 将堆顶元素对堆中最后一个元素交换
2. 将堆中有效数据个数减少一个
3. 对堆顶元素进行向下调整
public int poll() {int tmp = elem[0];swap(0,usesize-1);usesize--;signHeap(0,usesize);//0下标元素和最后一个元素换,之后只有0下标还不是大根堆,调整return tmp;}
8、PriorityQueue的特性
1. 使用时必须导入PriorityQueue所在的包,即:import java.util.PriorityQueue;
2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
ClassCastException异常(放入自定义元素类可以让该类继承Comparable并重写ComparTo方法,或者写比较器)例:
class Imp implements Comparator<Integer>{@Overridepublic int compare(Integer o1, Integer o2) {return o2.compareTo(o1);//大根堆}
}
public class Test1 {public int[] smallestK(int[] arr, int k) {//堆的默认比较时小根堆,我们建立大根堆需要比较器PriorityQueue<Integer> p=new PriorityQueue<Integer>(new Imp());
3. 不能插入null对象,否则会抛出NullPointerException
4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
5. 插入和删除元素的时间复杂度为
6. PriorityQueue底层使用了堆数据结构
7. PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素
9、堆排序
见下一篇博客,介绍所有排序