思路:从最后一个非叶子结点开始进行向下调整,如果父节点的值已经满足大于两个孩子结点的值,则直接跳出循环调整下一个结点;否则就与孩子结点进行交换,交换完之后可能会造成子树不满足堆的性质,因此需要继续向下调整。
建堆的时间复杂度为O(N)
向下调整的时间复杂度为O(logN)
代码:
//建立大根堆(向下调整)
public void createHeap() {
//从最后一个非叶子结点开始进行向下调整
for (int i = (usedsize-1-1) / 2; i >= 0; i--) {
shiftDown(i,usedsize);
}
}
public void shiftDown(int parent,int len) {
int child = 2 * parent + 1;
while (child < len) {
if (child + 1 < len && elem[child] < elem[child +1]) {
child = child + 1;
}
if (elem[child] > elem[parent]) {
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
parent = child;
child = 2 * parent + 1;
}else {
break;
}
}
}
思路:堆插入元素是直接插在有效元素的后面的【注定是最后一个叶子结点】,然后对这个叶子结点所在的位置进行向上调整,注意这个叶子不用在跟其兄弟叶子进行比较,只用跟其父结点进行比较。
向上调整的时间复杂度为O(logN)
向上调整建堆的时间复杂度为O(NlogN)
因此一般选用上下调整的算法建堆,因为最后2层的结点最多,要调整的元素也多,时间会变慢。
代码:
public void inset(int val) {
if (isFull()) {
addSize();
}
elem[usedsize++] = val;
//向上调整算法
shiftUp(usedsize-1);
}
public void shiftUp(int child) {
int parent = (child - 1) / 2;
while (parent >= 0) {
if (elem[parent] < elem[child]) {
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
child = parent;
parent = (child - 1) / 2;
}else {
break;
}
}
}
public boolean isFull() {
if (elem.length == usedsize) {
return true;
}
return false;
}
public void addSize() {
elem = Arrays.copyOf(elem,elem.length*2);
}
思路:每次删除的元素是堆顶的元素,将堆顶元素和堆中的最后一个元素进行交换,然后向下调整交换后的堆顶元素。
代码:
public void swap(int i, int j) {
int tmp = elem[i];
elem[i] = elem[j];
elem[j] = tmp;
}
public void deleElem() {
if (usedsize <= 0) {
return;
}
swap(0,usedsize-1);
usedsize--;
shiftDown(0,usedsize);
}
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(100);
底层代码:
分析:会创建一个初始容量为initialCapacity的优先级队列,这里创建的容量是100,注意初始容量不能小于1,否则会抛出异常。
LinkedList<Integer> list = new LinkedList<>();
list.add(1);
list.add(2);
list.add(3);
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(list);
分析:用LinkedList对象来构造一个优先级队列的对象。
注意:实例化优先级对象默认创建的是小根堆,如果是大根堆需要提供比较器【涉及到对象的比较】。
在Java中,基本数据类型可以大于、小于、等于去比较,但是对于对象的比较则需要用到以下几种方法。
①如果自定义的类没有重写euqals方法,那么默认是继承的Object类中的equals方法,使用的是“==”。
②自定义类重写equals方法
注意:euqals只能按照相等进行比较,不能比较大于、小于。
测试:
Student student1 = new Student("张三",22);
Student student2 = new Student("张三",22);
System.out.println(student1.equals(student2));
如果没有在Student类中重写euqals方法,则输出false;如果重写了euqals方法,则输出true。
注意:
①在接口的后面要加上泛型
②这种写法侵入性比较强,不易修改
class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
实例化构造器对象,并且调用构造器中的compare方法。
public static void main(String[] args) {
Student student1 = new Student("张三",22);
Student student2 = new Student("三",22);
AgeComparator ageComparator = new AgeComparator();
System.out.println(ageComparator.compare(student1, student2));
}
比较的方法 | 说明 |
---|---|
Object.equals | 不重写的话是继承Object的方法,需要重写覆写,只能比较相等与否 |
Comparable.compareTo | 需要手动实现接口,不够灵活 |
Comparator.compare | 需要实现一个比较器类对象 |
优先级队列默认是小根堆,如果要创建大根堆,那么就传入比较方法。
比较器:(注意用到泛型)
class IntCompare implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
实例化比较器、优先级队列对象,并传入比较器参数
public class MaxHeap {
public static void main(String[] args) {
IntCompare intCompare = new IntCompare();
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(intCompare);
priorityQueue.offer(3);
priorityQueue.offer(2);
priorityQueue.offer(9);
priorityQueue.offer(1);
System.out.println(priorityQueue);
}
}
力扣 17.14. 最小K个数
设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
示例:
输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]
思路:因为有大量的数据需要排序,因此采用堆这种数据结构,把所有的数据全部建成一个小根堆,然后每次取堆顶的元素。
代码:
class Solution {
public int[] smallestK(int[] arr, int k) {
int[] ret = new int[k];
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
//小根堆
for (int i = 0; i < arr.length; i++) {
priorityQueue.offer(arr[i]);
}
for (int i = 0; i < k; i++) {
ret[i] = priorityQueue.poll();
}
return ret;
}
}
思路:建立容量为k的大根堆,如果下一个待比较的元素比大根堆的元素小,就把大根堆的元素出堆,下一个待比较的元素进入堆中,这样最后得到的就是k个最小的元素。
【注意要判断数组为空 以及 k为0的情况,否则会出现空指针异常】
代码:
class IntComp implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
class Solution {
public int[] smallestK(int[] arr, int k) {
int[] ret = new int[k];
if(arr.length == 0 || k == 0) {
return ret;
}
//建立大根堆
IntComp intComp = new IntComp();
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(intComp);
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]);
}
}
for (int i = 0; i < k; i++) {
ret[i] = priorityQueue.poll();
}
return ret;
}
}