zoukankan      html  css  js  c++  java
  • Java的PriorityQueue

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6538654.html 

        优先队列实质上就是数据结构中的最小堆,而堆从概念图来看类似于一棵二叉树,从具体实现来说就是一个具有特别规律的数组,即:数组一个元素相当于堆中一个结点,而其左子结点、右子结点、父结点可以通过下标规律求出:

      leftNo = parentNo*2+1

      rightNo = parentNo*2+2

      parentNo = (nodeNo-1)/2

        这样,我们就可以看出:PriorityQueue实际上就是一个以某种比较关系来实现的最小堆,用数组来存放这个堆。PriorityQueue的创建、元素的增加、获取、删除都是对这个堆的操作,即对这个数组的操作。

     

       一:创建优先队列

        创建优先队列时,实际上就是新建了一个数组,默认大小11。

     private transient Object[] queue;  
      
    private static final int DEFAULT_INITIAL_CAPACITY = 11; 

       二:增加元素

      add(E e)offer(E e)都是向优先队列中插入元素,只是前者在插入失败时抛出异常,后者会返回false

        类似于数据结构中最小堆的操作:插入元素时,先插入成为堆的最后一个叶结点,然后根据比较关系把新结点往上调整,直到交换到合适位置。

    //offer(E e)
    public boolean offer(E e) {
        if (e == null)//不允许放入null元素
            throw new NullPointerException();
        modCount++;
        int i = size;
        if (i >= queue.length)
            grow(i + 1);//自动扩容
        size = i + 1;
        if (i == 0)//队列原来为空,这是插入的第一个元素
            queue[0] = e;
        else
            siftUp(i, e);//调整位置并插入新元素
        return true;
    }
    
    //siftUp()
    private void siftUp(int k, E x) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;//parentNo = (nodeNo-1)/2
            Object e = queue[parent];
            if (comparator.compare(x, (E) e) >= 0)//调用比较器的比较方法:这里的比较可以是元素的自然顺序,也可以是依靠比较器的顺序(自定义优先级)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }

      

      三:获取队首

        element()peek()都是获取但不删除队首元素,当方法失败时前者抛出异常,后者返回null。由于堆用数组表示,根据下标关系,0下标处的那个元素既是堆顶元素。所以直接返回数组0下标处的那个元素

    //peek()
    public E peek() {
        if (size == 0)
            return null;
        return (E) queue[0];//0下标处的那个元素就是最小的那个
    }

      

      四:队首出队

        remove()poll()都是获取并删除队首元素,当方法失败时前者抛出异常,后者返回null。由于删除操作会改变队列的结构,为了重新选出队首,需要对堆进行调整。

    public E poll() {
        if (size == 0)
            return null;
        int s = --size;
        modCount++;
        E result = (E) queue[0];//0下标即为队首
        E x = (E) queue[s];//把堆尾结点赋值给队首,令堆尾结点为null,相当于队首出队了
        queue[s] = null;
        if (s != 0)
            siftDown(0, x);//原理堆尾的结点现在位于堆顶,需要调整位置,交换到合适位置上,而与它进行交换的结点自然就会上升为堆顶,即队首
        return result;
    }
    
    //siftDown()
    private void siftDown(int k, E x) {
        int half = size >>> 1;
        while (k < half) {
            //首先找到左右孩子中较小的那个,记录到c里,并用child记录其下标
            int child = (k << 1) + 1;//leftNo = parentNo*2+1
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                break;
            queue[k] = c;//然后用c取代原来的值
            k = child;
        }
        queue[k] = x;
    }

      

      五:删除队中元素

        remove(Object o)方法用于删除队列中跟o相等的某一个元素。由于删除操作会改变队列结构,所以要进行调整堆的结点位置:1. 删除的是最后一个元素。直接删除即可,不需要调整。2. 删除的不是最后一个元素,则把堆尾的结点赋值给所删除元素的下标,再令堆尾为null(相当于删除了当前元素),最后位于当前位置的堆尾结点位置调整即可。 

    //remove(Object o)
    public boolean remove(Object o) {
        //通过遍历数组的方式找到第一个满足o.equals(queue[i])元素的下标
        int i = indexOf(o);
        if (i == -1)
            return false;
        int s = --size;
        if (s == i) //情况1
            queue[i] = null;
        else {
            E moved = (E) queue[s];
            queue[s] = null;
            siftDown(i, moved);//情况2
        }
        return true;
    }
    
    //siftDown()
    private void siftDown(int k, E x) {
        int half = size >>> 1;
        while (k < half) {
            //首先找到左右孩子中较小的那个,记录到c里,并用child记录其下标
            int child = (k << 1) + 1;//leftNo = parentNo*2+1
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                break;
            queue[k] = c;//然后用c取代原来的值
            k = child;
        }
        queue[k] = x;
    }
  • 相关阅读:
    使用Docker快速搭建PHP开发环境
    docker-compose 使用介绍
    Google广告显示不正确的问题
    Hexo博客写作与图片处理的经验
    Docker-compose 建立ELK集群
    Flink101-快速示例
    Spark学习笔记01-基础
    Java Metrics工具介绍
    跨界
    苹果 icloud 把我 ipad min 所有照片丢失
  • 原文地址:https://www.cnblogs.com/ygj0930/p/6538654.html
Copyright © 2011-2022 走看看