堆(heap)
1、基本概念
- 堆逻辑上是一棵完全二叉树
- 堆物理上是保存在数组中
- 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
- 反之,则是小堆,或者小根堆,或者最小堆
- 堆的基本作用是,快速找集合中的最值
2、堆的创建
我们以创建大堆为例
public static void adjustDown(int[]arr,int parent,int len){
int child = parent*2+1;
while (child < len){
if (child+1<len&&arr[child] <arr[child+1]){
child++;
}
if (arr[child] >arr[parent]){
int tmp = arr[child];
arr[child] = arr[parent];
arr[parent] = tmp;
parent = child;
child = parent*2+1;
}else {
break;
}
}
}
public static void creatheap(int[] arr){
int parent = (arr.length-1-1)/2;
for (int i = parent; i >=0 ; i--) {
adjustDown(arr,i,arr.length);
}
}
public static void main(String[] args) {
int[] arr = {27,15,19,18,28,34,65,49,25,37};
creatheap(arr);
System.out.println(Arrays.toString(arr));
}
堆的应用——优先级队列
public static void main(String[] args) {
PriorityQueue<Integer> pqueue = new PriorityQueue<>();
PriorityQueue<Integer> pqueue2 = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
pqueue.offer(27);
pqueue.offer(15);
pqueue.offer(17);
System.out.println(pqueue);
System.out.println(pqueue.peek());
System.out.println(pqueue.poll());
pqueue2.offer(27);
pqueue2.offer(15);
pqueue2.offer(17);
System.out.println(pqueue2);
System.out.println(pqueue2.peek());
System.out.println(pqueue2.poll());
}
//输出结果
[15, 27, 17]
15
15
[27, 15, 17]
27
27
优先级队列是一种数据结构,他的方法名和普通队列类似,但是实现却不同。Java中自带的类PriorityQueue默认是一个小堆,如果想自己变成大堆可以在调用构造方法的时候,在其中加入一个匿名内部类(new Comparator)。
1、入队操作
- 首先按尾插方式放入数组
- 比较其和其双亲的值的大小,如果双亲的值大,则满足堆的性质,插入结束
- 否则,交换其和双亲位置的值,重新进行 2、3 步骤
- 直到根结点
我们已自己实现的入队操作为例
public void adjustUp(int child){
int parent = (child-1)/2;
while (parent>=0){
if (elem[child]>elem[parent]){
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
child = parent;
parent = (child-1)/2;
}else {
break;
}
}
}
public void offer(int val){
if (elem.length == usedsize){
elem = Arrays.copyOf(elem,elem.length*2);
}
elem[usedsize] = val;
adjustUp(usedsize);
usedsize++;
}
2.出队操作
为了防止堆的结构被破坏,出队时会先让堆顶元素和堆尾元素互换,然后再弹出最后一个元素,最后再重新调整一边堆的结构。
public int poll(){
int tmp = elem[0];
elem[0] = elem[usedsize-1];
usedsize--;
adjustDown(0,usedsize);
return tmp;
}
Top-K问题
寻找一个数组里最大的三个数
寻找最大的K个数 建立小根堆 寻找第K大的数 建立小根堆
寻找最小的K个数 建立大根堆 寻找第K小的数 建立大根堆
public static void topK(int[] arr,int k){
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
for (int i = 0; i < k; i++) {
priorityQueue.offer(arr[i]);
}
for (int i = k; i < arr.length; i++) {
if (arr[i] > priorityQueue.peek()){
priorityQueue.poll();
priorityQueue.offer(arr[i]);
}
}
System.out.println(priorityQueue);
}
堆排序
堆排序的思路很简单,建立一个大根堆,然后重复出队操作就可以了。
public int[] heapsort(){
while (usedsize>0){
poll();
usedsize--;
}
return elem;
}