目录
- 一、堆的概念以及堆的创建
- 1.堆的概念
- 二.堆的创建
- 2.1向下遍历
- 2.2建堆操作
- 三. 堆方法的构建
- 3.1 入队列操作
- 3.2出队列操作
- 3.3查看头元素
- 四.优先级队列(堆)方法的使用
- 4.1堆的初始化(大堆和小堆创建)
- 五.堆排序(大堆)
一、堆的概念以及堆的创建
1.堆的概念
什么是堆?(堆的逻辑上是一个完全二叉树,实际上是保存在数组中)
我们把一个完全二叉树通过层序遍历存储到数组当中,这个数组就叫做堆。
注意:堆中的元素不能为空.(浪费大量空间)
什么是大根堆和小根堆?
大根堆是指根结点的关键字是堆里所有结点关键字中最大者
小根堆:根结点的值是所有结点值中最小者.
父亲结点为i时,左孩子的结点为 i* 2+1,右孩子的结点为 * 2+2;孩子结点为i时,父亲结点为(i-1)/2.
二.堆的创建
2.1向下遍历
对于堆中的排序我们可以采用向下遍历的方法,我们就拿小堆的向下遍历来做例子.
2.2建堆操作
由于堆的底层是数组,我们开始要创建一个数组并且初始化
public class TestHeap {public int[] elem;public int uesdSize;public TestHeap(){this.elem=new int[10];}
}
接下来我们要将一段无序的数组放进去,并且通过elem数组拷贝进来,同时我们通过由下向上不断遍历由上向下的函数
public void createBigHeap(int[] array){for(int i=0;i<array.length;i++){this.elem[i]=array[i];this.usedSize++;}for(int i=(this.usedSize-1-1)/2;i>=0;i--){shiftDown(i);}}
接下来我们编写由上向下的函数shiftDown(i);
public void shiftDown(int parent){int child=parent*2+1;while (child<this.usedSize){if(child+1<this.usedSize && this.elem[child+1]<this.elem[child]){child++;}if(this.elem[parent]>this.elem[child]){int temp=this.elem[parent];this.elem[child]=this.elem[parent];this.elem[parent]=temp;parent=child;child=child*2+1;}else {break;}}}
注意:一般来说for循环的时间复杂度为N,shiftDown的时间复杂度为logN,所以为O(N*logN),但是实际上为O(N);
三. 堆方法的构建
3.1 入队列操作
对于入队列操作我们采用向上遍历的方法。
我们入队列时,先判断是否满了,然后我们将入队列的值放入数组的最后一个,将它和他的父亲节点进行比较,若小于进行交换,一直循环到父亲节点小于0;
public boolean isFull(){return this.elem.length==this.usedSize;}public void push(int val){if(isFull()){this.elem=Arrays.copyOf(this.elem,2*this.elem.length);}this.elem[this.usedSize]=val;this.usedSize++;shiftUp(this.usedSize-1);}public void shiftUp(int child){int parent=(child-1)/2;while (parent>=0){if(this.elem[parent]>this.elem[child]){int temp=this.elem[parent];this.elem[parent]=this.elem[child];this.elem[child]=temp;child=parent;parent=(parent-1)/2;}else{break;}}}
3.2出队列操作
对于出队列的操作时,我们先让头和尾互换,然后将头向下遍历就行了.
public int pop(){if(isEmpty()){throw new RuntimeException("为空不能取出");}int temp=this.elem[0];this.elem[0]=this.elem[this.usedSize-1];this.elem[this.usedSize-1]=temp;this.usedSize--;shiftDown(0);return temp;}public boolean isEmpty(){return this.usedSize==0;}
3.3查看头元素
public boolean isEmpty(){return this.usedSize==0;} public int peek(){if(isEmpty()){throw new RuntimeException("为空不能取出");}return this.elem[0];}
四.优先级队列(堆)方法的使用
PriorityQueue是优先级队列
下面的它的方法和使用:
public static void main(String[] args) {PriorityQueue<Integer> pq=new PriorityQueue<>();pq.offer(1);pq.offer(2);pq.offer(3);System.out.println(pq.poll());System.out.println(pq.poll());System.out.println(pq.poll());//1 2 3}
4.1堆的初始化(大堆和小堆创建)
对于大堆和小堆的创建我们需要重写Comparator方法来进行操作.
我们使用匿名内部类进行编写详细方法如下
小堆:PriorityQueue<Integer> queue=new PriorityQueue<>(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o1-o2;}});大堆:PriorityQueue<Integer> queue=new PriorityQueue<>(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2-o1;}});
五.堆排序(大堆)
我们先将数组变成大堆,然后将第一个数和最后一个数进行交换,这时将最大的一个数就确定下来了,固定这个数,将第一个数进行排序,重复上述过程.
代码实现:
//堆排序public void heapSory(){int end=this.usedSize-1;while (end>0){int temp=this.elem[0];this.elem[0]=this.elem[end];this.elem[end]=temp;shiftDown01(0,end);end--;}}public void shiftDown01(int parent,int k){int child=parent*2+1;while (child<k){if(child+1<k && this.elem[child+1]>this.elem[child]){child++;}if(this.elem[child]>this.elem[parent]){int temp=this.elem[child];this.elem[child]=this.elem[parent];this.elem[parent]=temp;parent=child;child=child*2+1;}else{break;}}}