优先级队列PriorityQueue

优先级队列PriorityQueue,这名字听起来吊炸天,其实就是一个堆。

PriorityQueue 是一个基于优先级堆的无界队列, 它的元素是按照自然顺序(natural order)排序的。 在创建的时候, 我们可以给它提供一个负责给元素排序的比较器。 PriorityQueue 不允许 null 值, 因为他们没有自然顺序, 或者说他们没有任何的相关联的比较器。 最后, PriorityQueue 不是线程安全的, 入队和出队的时间复杂度是 O(log(n))。
堆树的定义如下:
(1) 堆树是一颗完全二叉树;
(2) 堆树中某个节点的值总是不大于或不小于其孩子节点的值;
(3) 堆树中每个节点的子树都是堆树。

Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示。
在这里插入图片描述
leftNo = parentNo2+1
rightNo = parentNo
2+2
parentNo = (nodeNo-1)/2
PriorityQueue的peek()和element操作是常数时间,add(), offer(), 无参数的remove()以及poll()方法的时间复杂度都是log(N)。

有关堆排序的知识,那么给一个数组,如何进行堆排序呢,同时这也是面试中会考察的:

package com.fwc;

public class heapsort {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//定义整型数组
        int[] arr = {1,5,6,8,7,2,3,4,9};
        //调用堆排序数组
        HeapSort(arr);
        //输出排序后的数组
        for(int i=0;i<arr.length;i++)
        {
            System.out.print(arr[i]+"  ");
        }
    }
    //堆排序函数
    public static void HeapSort(int[] arr)
    {
        int n = arr.length-1;
        for(int i=(n-1)/2;i>=0;i--)
        {
            //构造大顶堆,从下往上构造
            //i为最后一个根节点,n为数组最后一个元素的下标
            HeapAdjust(arr,i,n);
        }
        for(int i=n;i>0;i--)
        {
            //把最大的数,也就是顶放到最后
            //i每次减一,因为要放的位置每次都不是固定的
            swap(arr,i);
            //再构造大顶堆
            HeapAdjust(arr,0,i-1);
        }
    }

    //构造大顶堆函数,parent为父节点,length为数组最后一个元素的下标
    public static void HeapAdjust(int[] arr,int parent,int length)
    {
        //定义临时变量存储父节点中的数据,防止被覆盖
        int temp = arr[parent];
        //2*parent+1是其左孩子节点
        for(int i=parent*2+1;i<=length;i=i*2+1)
        {
            //如果左孩子大于右孩子,就让i指向右孩子
            if(i<length && arr[i]<arr[i+1])
            {
                i++;
            }
            //如果父节点大于或者等于较大的孩子,那就退出循环
            if(temp>=arr[i])
            {
                break;
            }
            //如果父节点小于孩子节点,那就把孩子节点放到父节点上
            arr[parent] = arr[i];
            //把孩子节点的下标赋值给parent
            //让其继续循环以保证大根堆构造正确
            parent = i;
        }
        //将刚刚的父节点中的数据赋值给新位置
        arr[parent] = temp;
    }

    //定义swap函数
    //功能:将跟元素与最后位置的元素交换
    //注意这里的最后是相对最后,是在变化的
    public static void swap(int[] arr,int i)
    {
        int temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
    }

	}




add(E e)和offer(E e)的语义相同,都是向优先队列中插入元素,只是Queue接口规定二者对插入失败时的处理不同,前者在插入失败时抛出异常,后则则会返回false。对于PriorityQueue这两个方法其实没什么差别

element()和peek()的语义完全相同,都是获取但不删除队首元素,也就是队列中权值最小的那个元素,二者唯一的区别是当方法失败时前者抛出异常,后者返回null。根据小顶堆的性质,堆顶那个元素就是全局最小的那个;由于堆用数组表示,根据下标关系,0下标处的那个元素既是堆顶元素。所以直接返回数组0下标处的那个元素即可

remove()和poll()方法的语义也完全相同,都是获取并删除队首元素,区别是当方法失败时前者抛出异常,后者返回null。由于删除操作会改变队列的结构,为维护小顶堆的性质,需要进行必要的调整

remove(Object o)方法用于删除队列中跟o相等的某一个元素(如果有多个相等,只删除一个),该方法不是Queue接口内的方法,而是Collection接口的方法。由于删除操作会改变队列结构,所以要进行调整;又由于删除元素的位置可能是任意的,所以调整过程比其它函数稍加繁琐。具体来说,remove(Object o)可以分为2种情况:

  1. 删除的是最后一个元素。直接删除即可,不需要调整。
  2. 删除的不是最后一个元素,从删除点开始以最后一个元素为参照调用一次siftDown()即可。此处不再赘述2. 删除的不是最后一个元素,从删除点开始以最后一个元素为参照调用一次siftDown()即可。此处不再赘述

猜你喜欢

转载自blog.csdn.net/u010651249/article/details/83863657