PriorityQueue 解析

PriorityQueue 解析Java1.5版本后就提供了一个具备了小根堆性质的数据结构也就是优先队列PriorityQueue。//PriorityQueue默认是一个小顶堆,然而可以通过传入自定义的Comparator函数来实现大顶堆。实际上是一个堆(不指定Comparator时默认为最小堆)队列既可以根据元素的自然顺序来排序,也可以根据 Comparator来设置排序规则。队列的头是按指定排序方式的最小元素…

大家好,又见面了,我是你们的朋友全栈君。

Java 1.5版本后就提供了一个具备了小根堆性质的数据结构也就是优先队列PriorityQueue。

//PriorityQueue默认是一个小顶堆,然而可以通过传入自定义的Comparator函数来实现大顶堆。

实际上是一个堆(不指定Comparator时默认为最小堆)
队列既可以根据元素的自然顺序来排序,也可以根据 Comparator来设置排序规则。
队列的头是按指定排序方式的最小元素。如果多个元素都是最小值,则头是其中一个元素。
新建对象的时候可以指定一个初始容量,其容量会自动增加。

注意1:该队列是用数组实现,但是数组大小可以动态增加,容量无限。

注意2:队列的实现不是同步的。不是线程安全的。如果多个线程中的任意线程从结构上修改了列表, 则这些线程不应同时访问 PriorityQueue实例。保证线程安全可以使用PriorityBlockingQueue 类。

注意3:不允许使用 null 元素。

注意4:插入方法(offer()、poll()、remove() 、add() 方法)时间复杂度为O(log(n)) ;
remove(Object) 和 contains(Object) 时间复杂度为O(n);
检索方法(peek、element 和 size)时间复杂度为常量。

注意5:方法iterator()中提供的迭代器并不保证以有序的方式遍历优先级队列中的元素。(原因可参考PriorityQueue的内部实现)
如果需要按顺序遍历,可用Arrays.sort(pq.toArray())。

注意6:可以在构造函数中指定如何排序。如:
PriorityQueue()
          使用默认的初始容量(11)创建一个 PriorityQueue,并根据其自然顺序来排序其元素(使用 Comparable)。
PriorityQueue(int initialCapacity)
          使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序来排序其元素(使用 Comparable)。
PriorityQueue(int initialCapacity, Comparator<? super E> comparator)
          使用指定的初始容量创建一个 PriorityQueue,并根据指定的比较器comparator来排序其元素。

注意7:此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选 方法。

PriorityQueue对元素采用的是堆排序,头是按指定排序方式的最小元素。堆排序只能保证根是最大(最小),整个堆并不是有序的。
方法iterator()中提供的迭代器可能只是对整个数组的依次遍历。也就只能保证数组的第一个元素是最小的。

 

//PriorityQueue默认是一个小顶堆,然而可以通过传入自定义的Comparator函数来实现大顶堆

PriorityQueue的数据结构

PriorityQueue 解析

PriorityQueue的逻辑结构是一棵完全二叉树,存储结构其实是一个数组。逻辑结构层次遍历的结果刚好是一个数组。

PriorityQueue的操作

add(E e) 和 offer(E e) 方法

add(E e) 和 offer(E e) 方法都是向PriorityQueue中加入一个元素,其中add()其实调用了offer()方法如下:

public boolean add(E e) {

        return offer(e);
    }
下面主要看看offer()方法的作用: 

这里写图片描述

如上图调用 offer(4)方法后,往堆中压入4然后从下往上调整堆为小顶堆。offer()的代码实现:

public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
      //如果压入的元素为null 抛出异常      
        int i = size;
        if (i >= queue.length)
            grow(i + 1);
            //如果数组的大小不够扩充
        size = i + 1;
        if (i == 0)
            queue[0] = e;
            //如果只有一个元素之间放在堆顶
        else
            siftUp(i, e);
            //否则调用siftUp函数从下往上调整堆。
        return true;
    }

private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x);
    }


当前元素与父节点不断比较如果比父节点小就交换然后继续向上比较,否则停止比较的过程。
private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
                break;
            queue[k] = e;//被比较的节点向父节点移动
            k = parent;//保存当前位置
        }
        queue[k] = key;
    }

poll() 和 remove() 方法 

poll 方法每次从 PriorityQueue 的头部删除一个节点,也就是从小顶堆的堆顶删除一个节点,而remove()不仅可以删除头节点而且还可以用 remove(Object o) 来删除堆中的与给定对象相同的最先出现的对象。先看看poll()方法。下面是poll()之后堆的操作

删除元素后要对堆进行调整: 

这里写图片描述

堆中每次删除只能删除头节点。也就是数组中的第一个节点。 
这里写图片描述 
将最后一个节点替代头节点然后进行调整。 。。。。。
 

public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

public E poll() {
        if (size == 0)
            return null;
      //如果堆大小为0则返回null      
        int s = --size;
        modCount++;
        E result = (E) queue[0];
        E x = (E) queue[s];
        queue[s] = null;
//如果堆中只有一个元素直接删除        
        if (s != 0)
            siftDown(0, x);
//否则删除元素后对堆进行调整            
        return result;
    }

private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
}

private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
        int half = size >>> 1;        // loop while a non-leaf
        while (k < half) {
            int child = (k << 1) + 1; // assume left child is least
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
                c = queue[child = right];
            if (key.compareTo((E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = key;
}

如果当前节点左右节点中的最小节点 小 break,否则一直与左右节点的最小节点交换。直到当前节点无子节点。

remove(Object o):

 

public boolean remove(Object o) {
        int i = indexOf(o);
        if (i == -1)
            return false;
        else {
            removeAt(i);
            return true;
        }
}

//O(n)
private int indexOf(Object o) {
        if (o != null) {
            for (int i = 0; i < size; i++)
                if (o.equals(queue[i]))
                    return i;
        }
        return -1;
}

private E removeAt(int i) {
        // assert i >= 0 && i < size;
        modCount++;
        int s = --size;
        if (s == i) // removed last element
            queue[i] = null;
        else {
            E moved = (E) queue[s];//树末端元素
            queue[s] = null;
            siftDown(i, moved);//从 当前删除位置向下比较
            if (queue[i] == moved) {
                siftUp(i, moved);//从当前位置向上比较
                if (queue[i] != moved)
                    return moved;
            }
        }
        return null;
}

自测用例:

public class A_05_PriorityQueue {
    public static void main(String[] args) {
        KthLargest kthLargest = new KthLargest(3, new int[]{4, 5, 8, 2});
//        System.out.println(kthLargest.add(3));
//        System.out.println(kthLargest.add(5));
//        System.out.println(kthLargest.add(10));
//        System.out.println(kthLargest.add(9));
//        System.out.println(kthLargest.add(4));

        PriorityQueue<Integer> mQueue = new PriorityQueue<>(3);
        mQueue.add(3);
        mQueue.add(2);
        mQueue.add(1);

        System.out.println("remove obj=="+mQueue.remove(Integer.valueOf((2))));
//        customMaxHeap();
    }

    private static void customMaxHeap() {
        //是用 自定义的compare
        PriorityQueue<People> queue = new PriorityQueue<People>(5,
                new Comparator<People>() {
                    public int compare(People p1, People p2) {
//                        return p2.age - p1.age;//Max heap
                        return p1.age - p2.age;//Min heap
                    }
                });

        for (int i = 1; i <= 10; i++) {
//            queue.add(new People("张"+ i, (new Random().nextInt(100))));
            People people = new People("张" + i, (new Random().nextInt(100)));
            queue.add(people);
            System.out.println("add..."+people.toString());
        }
        System.out.println("***********************");
        while (!queue.isEmpty()) {
            System.out.println(queue.poll().toString());
        }
    }
}

class KthLargest {

    //PriorityQueue默认是一个小顶堆,然而可以通过传入自定义的Comparator函数来实现大顶堆
    private final PriorityQueue<Integer> queue;
    private final int k;
    public KthLargest(int k, int[] nums) {
        this.k = k;
        queue = new PriorityQueue<>(k);
        for (int num:nums) {
            add(num);
        }
    }

    public int add(int val) {
        if (queue.size()<k){
            queue.add(val);
        }else if (val>queue.peek()){
            queue.poll();
            queue.offer(val);
        }
        return queue.peek();
    }
}

/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest obj = new KthLargest(k, nums);
 * int param_1 = obj.add(val);
 */

class People {
    String name;
    int age;
    public People(String name, int age){
        this.name = name;
        this.age = age;
    }
    public String toString() {
        return "姓名:"+name + " 年龄:" + age;
    }
}

 

參考文章:

https://www.cnblogs.com/gnivor/p/4841191.html

https://blog.csdn.net/u013309870/article/details/71189189

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/132869.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 用ccproxy + stunnel做个加密代理「建议收藏」

    用ccproxy + stunnel做个加密代理「建议收藏」https://www.stunnel.org/downloads.htmlccproxy+stunnel做个加密http代理和socks5代理目前国内用户无法访问某些国外网站,如http://

  • 怎么写xsd文件

    怎么写xsd文件1.最简单的Schema文档如何写一个最简单的XML Schema文档呢?首先,我们写出一个最简单的XML文档。hello.xml——————-version=”1.0″?> Hello World!!hello.xsd----------version=”1.0″?> xmlns:xsd=”http://www.w3.org/2

    2022年10月28日
  • 手眼标定理解「建议收藏」

    手眼标定理解「建议收藏」参考:https://blog.csdn.net/yaked/article/details/77161160?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control什么是手眼标定手眼标定是为了得到相机坐标系

  • Html背景图片_html背景图片平铺

    Html背景图片_html背景图片平铺这里面有许多简洁的html背景界面,对,重要的是简洁,而且很好看,verynice:https://www.toptal.com/designers/subtlepatterns/可能需要梯子。网站提供一键预览:   …

  • YUV图像合成原理[通俗易懂]

    YUV图像合成原理[通俗易懂]YUV图像合成原理引言:在视频监控中最常用的就是图像拼接和字符叠加,25FPS的视频流,如果每隔40MS就从各个通道中取一幅图像来合成,则可以看到一个实时的合成视频。合成的过程也就是原始图像的拼接、缩放的过程,本文主要阐述UV分开存储的YUV420图像拼接的过程,实现下图的效果。一、原图图像格式1、图像常用的格式有两种RGB和YUV(1)YUV是

  • matplotlib绘图基础[通俗易懂]

    matplotlib绘图基础[通俗易懂]http://blog.csdn.net/pipisorry/article/details/37742423matplotlib介绍matplotlib是python最著名的绘图库,它提供了一整套和matlab相似的命令API,十分适合交互式地进行制图。而且也可以方便地将它作为绘图控件,嵌入GUI应用程序中。它的文档相当完备,并且Gallery页面中有上百幅缩略图,打开之后都

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号