PriorityQueue介绍

    • PriorityQueue
    • 堆的应用
      • 找前k个最小数据(TOPK问题)
      • 求k个最小的数优化
      • 堆排序

PriorityQueue

Java集合框架中提供了PriorityQueue和PriorityBlockingQueue(优先级阻塞队列)两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的。
PriorityQueue介绍_第1张图片

PriorityQueue介绍_第2张图片

常用方法:
PriorityQueue介绍_第3张图片
1:当你没传入比较器时候;PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出ClassCastException异常(如果放自定义类型;可以通过实现compareable接口;重写compareTo方法;传比较器优先级比较高)

class Student implements Comparable<Student>{
    public int age;
    public Student(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Student o) {
        //return this.age - o.age;
        return this.age - o.age;
    }
}

2:不能插入null对象,否则会抛出NullPointerException
3:入和删除元素的时间复杂度为log(N)
4:PriorityQueue默认情况下是小堆—即每次获取到的元素都是最小的元素
5:使用时必须导入PriorityQueue所在的包,即:importjava.util.PriorityQueue;

默认是小堆;如果想要大堆;如何变成大堆?
重写compareTo方法逻辑:

  @Override
    public int compareTo(Student o) {
        //return this.age - o.age;
        return o.age - this.age;
    }

PriorityQueue还有其它构造方法;比如传比较器的构造方法:PriorityQueue queue = new PriorityQueue<>(Comparator comparator);
这个构造方法创建一个空的优先级队列,并使用指定的比较器来确定元素的排序顺序。(这个使用比较器的指定顺序是比较优先的;还有一个构造方法是通过集合类和比较器构建优先级队列;那么不会用默认的比较方法;而是比较器的)

class IntCmp implements Comparator<Integer> {

    @Override
    public int compare(Integer o1, Integer o2) {
        //return o2-o1;
        return o2.compareTo(o1);
    }
}

PriorityQueue priorityQueue = new PriorityQueue<>(new IntCmp());

扩容机制:满了它会自动扩容的
如果容量小于64时,是按照oldCapacity的2倍方式扩容的
如果容量大于等于64,是按照oldCapacity的1.5倍方式扩容的
如果容量超过MAX ARRAY SIZE(MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8),按照MAX ARRAY_SIZE来进行扩容。
在 Java 中,数组是由连续的内存空间组成的数据结构,而每个对象都会占用一定的内存空间。因此,在数组中存储对象时,需要考虑额外的内存开销。
在 PriorityQueue 中,减去 8 的目的是为了留出足够的空间,以容纳对象引用和一些额外的内部数据,如对象头信息等。这些额外的开销包括。

匿名内部类写这个代码:

 PriorityQueue<Student> priorityQueue = new PriorityQueue<>(2, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o2.age - o1.age;
            }
        });

堆的应用

找前k个最小数据(TOPK问题)

最大或者最小的前k个数据。有10W个数据,让你找到前K个最大/最小的数据。
粗暴法:

  //   最大或者最小的前k个数据。有10W个数据,让你找到前K个最大/最小的数据。
    public int[] smallstk(int []arr,int k){
        Arrays.sort(arr);
        int[] tmp=new int[k];
        for (int i = 0; i <k ; i++) {
            System.out.println(arr[i]);
            tmp[i]=arr[i];
        }
        return tmp;
    }

提问:Arrays.sort(arr);底层是什么呢?
TimSort”的混合排序算法。TimSort 是结合了归并排序和插入排序的排序算法:
TimSort 的底层细节包括以下关键步骤:
1:数据分块;输入数组被分成多个小块(或称为"run"),每个小块都是已经排好序的。
2:合并排序;TimSort 使用归并排序来合并这些小块,将它们逐步合并成较大的块,直到得到一个完全有序的数组。
3:插入排序;在合并过程中,TimSort 使用插入排序来进一步优化性能,确保排序操作尽可能高效。

小堆实现:存进堆里;然后再取出来

    //使用小堆实现
    public int [] smallstk1(int []arr,int k){
        PriorityQueue<Integer> priorityQueue=new PriorityQueue<>();
        for (int i = 0; i <arr.length ; i++) {
            priorityQueue.offer(arr[i]);
        }
        int []tmp=new int[k];
        for (int i = 0; i < k; i++) {
            tmp[i]=priorityQueue.poll();
            
        }
        return tmp; 
    }

时间复杂度nlogn,第一个循环复杂度O(n)第二个循环的复杂度你nlogn;复杂度这方面确实不是很行。能否优化一下呢?

求k个最小的数优化

逻辑:1:先将最先k个元素建立大堆
2:从k后面开始就每一次往后走比较堆顶元素和数组元素大小,如果堆顶比较小就没什么事直接数组往后走。
如果堆顶比较大,就把堆顶弹出(它会自动调整为大堆),并且把这个数组元素加入进去(让它自动调整为大堆)再往后走

    public static int[] smallestK3(int[] arr, int k) {
        //不加这句会报异常,源码
        if(arr == null || k == 0) {
            return new int[0];
        }
        //1. 建立一个大根堆
        PriorityQueue<Integer> minHeap = new PriorityQueue<>(k, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });

        //2、
        for (int i = 0; i < arr.length; i++) {
            if(minHeap.size() < k) {
                minHeap.offer(arr[i]);
            }else {
                //当前数组的元素是arr[i]    18
                int val = minHeap.peek();  //27
                if(val > arr[i]) {
                    //弹出大的
                    minHeap.poll();
                    //放进小的
                    minHeap.offer(arr[i]);
                }
            }
        }

        //3 最把这个堆的全部元素弹给数组记录下来
        int[] tmp = new int[k];
        for (int i = 0; i < k; i++) {
            tmp[i] = minHeap.poll();
        }
        return tmp;
    }

堆排序

如果要排序一组数,从小到大(让下标有序):
使用小堆:这是不可能实现的;每次弹出最小的没有问题;但是放到哪去;如果放别的地方;空间复杂度就变大了;但是小堆,你也不一定就有序,左右谁大谁小不知道。
使用大堆:每次堆顶和最后一个交换。然后它再自动的排序好大堆。然后我们就不能包含这个最后的元素。交换位置由最后一个元素往前走一步(反之排序建立小堆)我都不用弹出处理交换,直接在原来数组交换。换完排序就好了。所以这才叫堆排序。
代码:

    //堆排序
    public void heapSort(int []array){
        int usedSize=array.length;//usedSize是有效元素个数
        int end=usedSize-1;
        while (end>0){
            //交换0位置和最后的位置;最后的位置放最大值;每次往前走
            int tmp=elem[0];
            elem[0]=elem[end];
            elem[end]=tmp;
            
            shiftDown(0,end);
            end--;//end传的是数组元素下标,10个元素,我减1。,是不是只调整9个元素。每次结束就少一个元素调整(end--)
        }

    }

时间复杂度:建立堆的复杂度O(n)
O(n) +O(nlogn)约等于O(nlogn)
空间复杂度O(1);没有浪费,创建额外的空间

你可能感兴趣的:(数据结构,java,开发语言)