堆排序的应用很广泛,但是当我们往堆中添加完元素,然后更改这些元素的属性,更改后的元素并不会重新形成堆,这时候需要我们自己去实现堆的动态调整了,因为很多语言并未实现这一步骤,例如java中的PriorityQueue就没有动态调整。
思路:要重新调整其实不难,对调整后的元素分别进行向上和向下的堆调整,它能上则上,不能上则下,两条路二选一,当它来到合适的位置则调整完毕。问题是我们怎么知道它的索引呢?直接根据ArrayList的indexOf就进行了O(n)的循环,所以我们加了一个Map来同步更新索引,这样就直接通过map就get到下标了,避免了O(n)的一步,具体代码如下:
package class04;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
/**
* @Description 堆的动态调整
* @Package: class04
* @Author: Ray
* @CreateTime: 2020/7/14 22:36
* @E-mail: 634302021@qq.com
*/
public class Test4 {
public static class Student {
public int classNo;
public int age;
public int id;
public Student(int c, int a, int i) {
classNo = c;
age = a;
id = i;
}
}
public static class MyHeap<T> {
public ArrayList<T> heap;
public Comparator<? super T> comparator;
public int heapSize; //堆的大小,动态维护,同时是indexMap的value
public HashMap<T, Integer> indexMap;
public MyHeap(Comparator<T> c) {
this.heap = new ArrayList<>();
this.comparator = c;
this.heapSize = 0;
this.indexMap = new HashMap<>();
}
/**
* 堆添加元素
* @param o
*/
public void push(T o) {
heap.add(o);
//每加一个,同步添加到indexMap
indexMap.put(o, heapSize);
//每加一个,做向上调整
heapUp(heapSize);
heapSize += 1;
}
/**
* 取堆顶返回,并将堆底放置堆顶,重新形成小根堆
* @return
*/
public T pop() {
T ans = heap.get(0);
//首尾交换
swap(0, heapSize - 1);
//indexMap和heap移除最后一个(原来的首)
indexMap.remove(ans);
heap.remove(ans);
//向下调整
heapDown(0, --heapSize);
return ans;
}
/**
* 堆动态调整
* @param o
*/
public void resign(T o) {
int index = indexMap.get(o);
heapUp(index);
heapDown(index, heapSize);
}
/**
* 堆向上调整
* @param index
*/
public void heapUp(int index) {
//如果下面小,上面大,就交换,形成小根堆
while (comparator.compare(heap.get(index), heap.get((index - 1) / 2)) < 0) {
swap(index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
/**
* 堆向下调整
* @param index
* @param heapSize
*/
public void heapDown(int index, int heapSize) {
int left = index * 2 + 1;
//如果下方有左树,没越界
while (left < heapSize) {
//存在右孩子且右<左,取右孩子下标给smaller,否则把左孩子下标给smaller
int smaller = left + 1 < heapSize && comparator.compare(heap.get(left + 1), heap.get(left)) < 0 ? left + 1 : left;
//如果上大下小,那么交换,否则已经是小根堆了无需再调整
if (comparator.compare(heap.get(index), heap.get(smaller)) > 0) {
swap(index, smaller);
} else {
break;
}
index = smaller;
left = index * 2 + 1;
}
}
/**
* 交换位置
* @param i
* @param j
*/
public void swap(int i, int j) {
T o1 = heap.get(i);
T o2 = heap.get(j);
heap.set(i, o2);
heap.set(j, o1);
indexMap.put(o1, j);
indexMap.put(o2, i);
}
/**
* 是否为空
* @return
*/
public boolean isEmpty() {
return heapSize == 0;
}
}
/**
* 根据年龄比较
*/
public static class StudentComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
public static void main(String[] args) {
Student s1 = null;
Student s2 = null;
Student s3 = null;
Student s4 = null;
Student s5 = null;
Student s6 = null;
s1 = new Student(2, 10, 11111);
s2 = new Student(1, 20, 22222);
s3 = new Student(6, 30, 33333);
s4 = new Student(3, 40, 44444);
s5 = new Student(7, 50, 55555);
s6 = new Student(1, 60, 66666);
MyHeap<Student> myHeap = new MyHeap<>(new StudentComparator());
myHeap.push(s1);
myHeap.push(s2);
myHeap.push(s3);
myHeap.push(s4);
myHeap.push(s5);
myHeap.push(s6);
s2.age = 80;
myHeap.resign(s2);
s4.age = 13;
myHeap.resign(s4);
s5.age = 14;
myHeap.resign(s5);
s6.age = 15;
myHeap.resign(s6);
while (!myHeap.isEmpty()) {
Student cur = myHeap.pop();
System.out.println(cur.classNo + "," + cur.age + "," + cur.id);
}
}
}