• 堆的基本操作和PriorityQueue接口


    目录

    堆的插入

    堆的删除

    PriorityQueue接口

    PriorityQueue的注意事项:

    PriorityQueue常用接口介绍

      1. 优先级队列的介绍

      2.  扩容

      3. 插入/删除/获取优先级最高的元素

    Java对象的比较

           1.基本类型的比较

           2.对象比较的问题

           3.对象比较的方法

    Top- k问题


    堆的插入

    堆的插入总共分为两个步骤:

            1:先将元素放到底层空间中(空间不够的时候要扩容)

            2:将新插入的节点向上调整,直到满足大根堆或者小根堆的性质(以大根堆为例)

    代码实现:

    1. public void offer(int val) {
    2. if(isFull()) {
    3. //扩容
    4. Arrays.copyOf(elem,2*elem.length);
    5. }
    6. //放到最后一个位置
    7. elem[usedSize++] = val;
    8. //向上调整
    9. shiftUp(usedSize- 1);
    10. }
    11. //判断是否满了
    12. public boolean isFull() {
    13. return usedSize == elem.length;
    14. }
    15. //向上调整
    16. private void shiftUp(int child) {
    17. int parent = (child-1) / 2;
    18. while(child > 0) {
    19. if(elem[child] > elem[parent]) {
    20. int tmp = elem[child];
    21. elem[child] = elem[parent];
    22. elem[parent] = tmp;
    23. child = parent;
    24. parent = (child-1) / 2;
    25. } else {
    26. break;
    27. }
    28. }
    29. }

    向上调整建堆的时间复杂度:


     堆的删除

    注意:堆的删除一定是删除堆顶元素

            1: 将堆顶元素和堆中有效数据的最后一个数据交换

            2: 将有效长度减1(最后一个元素就被删除)

            3: 对堆顶元素进行向下调整

    代码实现:

    1. public void pop(int val) {
    2. if(isEmpty()) {
    3. return;
    4. }
    5. int tmp = elem[0];
    6. elem[0] = elem[usedSize-1];
    7. elem[usedSize-1] = tmp;
    8. usedSize--;
    9. shifDown(0,usedSize);
    10. }
    11. //判断是否为空
    12. public boolean isEmpty() {
    13. return usedSize == 0;
    14. }
    15. //向下调整
    16. public void shifDown(int parent,int len) {
    17. int child = 2 * parent + 1;
    18. //最起码有一个左孩子
    19. while (child < len) {
    20. //有右孩子的情况
    21. if (child + 1 < len && elem[child] < elem[child + 1]) {
    22. //保证child下标是左右孩子最大值下标
    23. child++;
    24. }
    25. if (elem[child] > elem[parent]) {
    26. int tmp = elem[child];
    27. elem[child] = elem[parent];
    28. elem[parent] = tmp;
    29. parent = child;
    30. child = 2 * parent + 1;
    31. } else {
    32. break;
    33. }
    34. }
    35. }

    PriorityQueue接口

    java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的. 本文主要介绍PriorityQueue.


    PriorityQueue的注意事项:

    1. 在使用PriorityQueue时要导入所在包

    2. PriorityQueue中放置的元素必须能够比较大小,不能插入无法比较大小的对象,否则会抛出classCastException.

    3. 不能插入null对象,否则会抛出NUllPointException.

    4. 没有容量限制,可以任意插入多个元素,其内部可以自动扩容

    5. 插入和删除的时间复杂度为O(log2 N)

    6. PriorityQueue底层使用了堆数据结构

    7. PriorityQueue默认底层为小根堆.每次获取到的都是最小的元素.


    PriorityQueue常用接口介绍

            1. 优先级队列的介绍

    代码实现 :

      一般在创建优先级队列对象时,如果知道元素个数,建议就直接将底层容量给好
      否则在插入时需要扩容
    扩容机制:开辟更大的空间,拷贝元素,这样效率会比较低
         

      2.  扩容

      优先级队列的扩容说明 :
            1. 如果容量小于64时,按照oldCapacity的2倍扩容
            2. 如果容量大于64时,按照oldCapacity的1.5倍扩容
            3. 如果容量大于MAX_ARRAY_SIZE,按照MAX_SIZE_ARRAY来进行扩容

      3. 插入/删除/获取优先级最高的元素

    我们在调用这个接口的时候,这些方法可以直接被使用.


         

    Java对象的比较

           1.基本类型的比较

    在Java中,基本数据类型可以直接进行比较

    运行结果:

           

             2.对象比较的问题

    从编译结果来看,Java中引用类型的变量不能直接使用 > 或者 <的方式进行比较,那 == 为什么可以呢?

    因为:用户实现自定义类型,都默认继承自Object类,而Object类提供了equal方法,而==默认情况下调用的就是equal方法,但是该方法的比较规则是:没有比较引用变量引用的对象,而是直接比较引用变量的地址.

            3.对象比较的方法

            1.重写equals方法

    在上面创建的两个Student对象,他们的名字和年龄都相同,意味着他俩是同一个Student.而在重写equals方法中只要年龄和名字相同就返回true.

    equal只能按照相等进行比较,本能按照大于 , 小于的方式进行比较.

           

             2.基于Comparable接口类的比较

    如果想按照大小的方式进行比较,在自定义类时,实现Comparble接口即可,然后重写compareTo方法.但这种方法还是存在缺陷,就像上面代码显示一样,只能按照年龄进行比较,不能按照其他变量进行比较.

           

            3.基于比较器的比较

    代码表示: 

    我们想用什么变量比较就实现Comparble接口即可,然后重写compareTo方法.


    Top- k问题

    TOP-K问题:即求数据集合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大.

    思路:

    1. 用数据集合中的前 k 个元素来建堆

            ● 前k个最大元素 :建小根堆

            ●前k个最小元素 : 建大根堆

    2. 用剩余的N-k个元素和堆顶元素进行比较 , 不满足则替换堆顶元素 ,

    将剩余的N-k 个元素依次和堆顶元素比较 , 堆中剩余的元素就是前K个最大的或者最小的元素.

    用比较器构造大根堆和小根堆

    代码实现前k个最大元素 :

    java中默认使用的是小根堆,在求前k个最大元素的时候可以不使用比较器建小根堆

    1. public int[] maxK(int[] arr,int k) {
    2. int ret[] = new int[k];
    3. if(arr == null || k == 0) {
    4. return ret;
    5. }
    6. //建立K个元素的小根堆
    7. Queue<Integer> minHeap = new PriorityQueue<>(k);
    8. //遍历数组的前K个,放到堆中
    9. for (int i = 0; i < k; i++) {
    10. minHeap.offer(arr[i]);
    11. }
    12. //遍历剩下的k-1个,和堆顶元素比较
    13. // 堆顶元素小的时候就出堆
    14. for (int i = k; i < arr.length; i++) {
    15. int val = minHeap.peek();
    16. if(val < arr[i]) {
    17. minHeap.poll();
    18. minHeap.offer(arr[i]);
    19. }
    20. }
    21. for (int i = 0; i < k; i++) {
    22. ret[i] = minHeap.poll();
    23. }
    24. return ret;
    25. }

    代码实现前k个最小元素 :

    1. public class TestDemo<E> {
    2. //求最小的K个数,通过比较器创建大根堆
    3. public int[] smallestK(int[] array, int k) {
    4. if (k <= 0) {
    5. return new int[k];
    6. }
    7. GreaterIntComp greaterCmp = new GreaterIntComp();
    8. PriorityQueue<Integer> maxHeap = new PriorityQueue<>(greaterCmp);
    9. //先将前K个元素,创建大根堆
    10. for (int i = 0; i < k; i++) {
    11. maxHeap.offer(array[i]);
    12. }
    13. //从第K+1个元素开始,每次和堆顶元素比较
    14. for (int i = k; i < array.length; i++) {
    15. int top = maxHeap.peek();
    16. if (array[i] < top) {
    17. maxHeap.poll();
    18. maxHeap.offer(array[i]);
    19. }
    20. }
    21. //取出前K个
    22. int[] ret = new int[k];
    23. for (int i = 0; i < k; i++) {
    24. int val = maxHeap.poll();
    25. ret[i] = val;
    26. }
    27. return ret;
    28. }
    29. }
    30. }

  • 相关阅读:
    AE 的软件、硬件、驱动控制、调试策略(没有算法)
    【Nginx27】Nginx学习:代理模块(一)基本配置与概念
    java二手车商城计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
    Java项目实战【超级详细】
    【C++】运算符重载 ④ ( 一元运算符重载 | 使用 全局函数 实现 前置 ++ 自增运算符重载 | 使用 全局函数 实现 前置 - - 自减运算符重载 )
    数值分析思考题(钟尓杰版)参考解答——第五章
    Flyme预计明年上车!星纪时代携手魅族,推动智行生态完美融合
    MFC:AfxMessageBox函数随记
    Docker网络+资源控制
    “通胀噩梦:恶梦继续还是即将终结?经济前景备受关注!“
  • 原文地址:https://blog.csdn.net/2301_76692760/article/details/133040869