【12】优先级队列的介绍和实现

目录

1、介绍

2、优先级队列的接口定义

2.1、队列基础接口

3、基于无序数组实现优先级队列

4、基于有序数组实现优先级队列

5、基于堆实现优先级队列


1、介绍

优先级队列是指在获取队列中的数据时,按照每一个数据的优先级进行获取,不按照先进先出的规则获取了。那么每一个数据都必须有一个相应的优先级。

优先级队列添加数据和普通队列没有区别,但是在获取数据时就不是获取队列头部的数据了,而是获取优先级最高的数据。

2、优先级队列的接口定义

2.1、队列基础接口

和普通队列相比,优先级队列就是在满足普通队列的所有需求。

/**
 * 队列基础接口
 *
 * @author zjj_admin
 */
public interface Queue {
​
​
    /**
     * 向队列的尾部插入数据
     *
     * @param value 添加数据
     * @return 是否添加成功
     */
    boolean offer(E value);
​
    /**
     * 从队列的头部拉取一个数据,并从队列中移除
     *
     * @return 返回头部数据,没有数据时返回 null
     */
    E poll();
​
    /**
     * 获取队列头部数据,不移除数据
     *
     * @return
     */
    E peek();
​
    /**
     * 判断队列是否为空
     *
     * @return 队列是否为空
     */
    boolean isEmpty();
​
    /**
     * 队列是否已经满了
     *
     * @return 队列是否已满
     */
    boolean isFull();
​
    /**
     * 获取队列中元素的个数
     *
     * @return 元素个数
     */
    int size();
​
​
}

2.2、优先级接口定义

/**
 * @author zjj_admin
 */
public interface Priority {
​
    /**
     * 优先级,越大说明优先级越高
     *
     * @return
     */
    int priority();
}

3、基于无序数组实现优先级队列

使用无序数组,在数组中存储的数据是没有顺序的,所以在获取队列元素时需要手动找到优先级最大的元素。

在添加数据时比较方便。但是在获取数据时复杂一些。

/**
 * 优先级队列添加数据和普通队列没有区别,但是在获取数据时就不是获取队列头部的数据了,而是获取优先级最高的数据。
 * 

* 基于无序数组实现优先级队列 * 使用无序数组,在添加数据时比较方便。但是在获取数据时复杂一些。 * * @author zjj_admin */ public class ArrayPriorityQueue1 implements Queue { ​ ​    private final Priority[] array;    private int size; ​    public ArrayPriorityQueue1(int capacity) {        size = 0;        array = new Priority[capacity];   } ​    /**     * 添加数据     *     * @param value 添加数据     * @return     */    @Override    public boolean offer(E value) {        if (isFull()) {            return false;       }        //向数组的尾部添加一个数据即可        array[size] = value;        size++;        return true;   } ​ ​    /**     * 移除并返回优先级最高的一个数据     *     * @return     */    @Override    public E poll() {        if (isEmpty()) {            return null;       }        //获取优先级最大的元素下标        int max = selectMaxPriority();        @SuppressWarnings("all")        E value = (E) array[max];        //从索引 max + 1 开始,将后面的数据整体向前移动 1 位        System.arraycopy(array, max + 1, array, max, size - max - 1);        size--;        return value;   } ​    /**     * 获取优先级最大的元素下标     *     * @return     */    private int selectMaxPriority() {        int max = Integer.MIN_VALUE;        int maxIndex = 0;        for (int i = 1; i < size; i++) {            int priority = array[i].priority();            if (priority > max) {                max = priority;                maxIndex = i;           }       }        return maxIndex;   } ​ ​    /**     * 返回优先级最高的一个数据     *     * @return     */    @Override    public E peek() {        if (isEmpty()) {            return null;       }        //获取优先级最大的元素下标        int max = selectMaxPriority();        @SuppressWarnings("all")        E value = (E) array[max];        return value;   } ​    @Override    public boolean isEmpty() {        return size == 0;   } ​    @Override    public boolean isFull() {        return size == array.length;   } ​    @Override    public int size() {        return size;   } }

4、基于有序数组实现优先级队列

使用有序数组,优先级高的数据存放在数组的最后,每一次获取数据时直接那数组中最后一个数据即可。

这样在获取数据时比较简单,进而添加数据时就比较复杂了。需要将插入数据存放在合适的位置。

/**
 * 

* 基于有序数组实现优先级队列 * 使用有序数组,优先级高的数据存放在数组的最后,每一次获取数据时直接那数组中最后一个数据即可。 * 这样在获取数据时比较简单,进而添加数据时就比较复杂了。需要将插入数据存放在合适的位置。 * * @author zjj_admin */ public class ArrayPriorityQueue2 implements Queue { ​ ​    private final Priority[] array;    private int size; ​    public ArrayPriorityQueue2(int capacity) {        size = 0;        array = new Priority[capacity];   } ​    /**     * 添加数据     *     * @param value 添加数据     * @return     */    @Override    public boolean offer(E value) {        if (isFull()) {            return false;       }        // 从后向前找,获取第一个优先级小于插入数据的下标索引        int target = getTargetIndex(value.priority());        if (target == 0) {            // 当 target = 0 时,将所有的数据整体后移动一位            System.arraycopy(array, 0, array, 1, size);            array[0] = value;       } else {            //从索引 target + 1 开始,将后面的数据整体向后移动一位            // 这里也可以采用插入排序算法进行修改            System.arraycopy(array, target + 1, array, target + 2, size - target - 1);            array[target + 1] = value;       }        size++;        return true;   } ​    /**     * 从后向前找,获取第一个优先级小于插入数据的下标索引     *     * @return     */    private int getTargetIndex(int priority) {        for (int i = size - 1; i >= 0; i--) {            if (array[i].priority() < priority) {                return i;           }       }        // 最后就返第一个即可        return 0;   } ​ ​    /**     * 移除并返回优先级最高的一个数据     *     * @return     */    @Override    public E poll() {        if (isEmpty()) {            return null;       }        //直接获取最后一个数据即可        @SuppressWarnings("all")        E value = (E) array[size - 1];        //删除元素        size--;        return value;   } ​ ​    /**     * 返回优先级最高的一个数据     *     * @return     */    @Override    public E peek() {        if (isEmpty()) {            return null;       }        //直接获取最后一个数据即可        @SuppressWarnings("all")        E value = (E) array[size - 1];        return value;   } ​    @Override    public boolean isEmpty() {        return size == 0;   } ​    @Override    public boolean isFull() {        return size == array.length;   } ​    @Override    public int size() {        return size;   } }

5、基于堆实现优先级队列

计算机科学中,堆是一种基于树的数据结构,通常用完全二叉树实现。堆的特性如下

  • 在大顶堆中,任意节点 C 与它的父节点 P 符合 P.value >= C.value

  • 而小顶堆中,任意节点 C 与它的父节点 P 符合 P.value <= C.value

  • 最顶层的节点(没有父亲)称之为 root 根节点

优先级队列使用大根堆更加合适,即每一个节点的值不小于孩子节点。因为优先级队列每一个获取都是获取优先级最高的数据,所以使用大根堆更加方便。

/**
 * 堆一般看做一个完全二叉树
 * 优先级队列使用大根堆更加合适,即每一个节点的值不小于孩子节点。
 * 因为优先级队列每一个获取都是获取优先级最高的数据,所以使用大根堆更加方便。
 *
 * @author zjj_admin
 */
public class HeapPriorityQueue implements Queue {
​
​
    private final E[] array;
    private int size;
​
    @SuppressWarnings("all")
    public HeapPriorityQueue(int capacity) {
        size = 0;
        array = (E[]) new Priority[capacity];
    }
​
    /**
     * 添加数据
     * 步骤:
     * 1、将新添加的元素存在完全二叉树的最后一个节点
     * 2、调整堆的结构让其满足大根堆的特点
     *
     * @param value 添加数据
     * @return
     */
    @Override
    public boolean offer(E value) {
        if (isFull()) {
            return false;
        }
        if (size == 0) {
            array[size] = value;
            size++;
            return true;
        }
        //获取添加节点的索引
        int addIndex = size;
        size++;
        //获取父节点的索引
        int parentIndex = (addIndex - 1) / 2;
        //对堆的结构进行调整,让其满足大根堆的特性。
        while (addIndex > 0 && array[parentIndex].priority() < value.priority()) {
            array[addIndex] = array[parentIndex];
            addIndex = parentIndex;
            parentIndex = (addIndex - 1) / 2;
        }
        array[addIndex] = value;
​
        return true;
    }
​
​
    /**
     * 移除并返回优先级最高的一个数据
     * 步骤:
     * 1、获取第一个数据,第一个数据及时需要移除的数据。
     * 2、将第一个数据和最后一个数据进行交换,让需要移除的数据放在数组的最后面。
     * 3、将堆的最后一个数据移除
     * 4、将根节点按照大根堆的特点调整位置,让其满足大根堆。
     *
     * @return
     */
    @SuppressWarnings("all")
    @Override
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        //获取需要移除的位置
        E removed = array[0];
        //将数组的长度减少一个单位
        size--;
        //将最后一个放在第一个位置
        array[0] = array[size];
        //将最后一个数据设置为 null ,方便垃圾回收
        array[size] = null;
        //调整,是指满足大根堆的条件,做下潜操作
        down(0);
        return removed;
    }
​
    /**
     * 下潜操作,知道满足大根堆的条件
     *
     * @param index
     */
    private void down(int index) {
        if (2 * index + 1 < size && array[index].priority() < array[2 * index + 1].priority()) {
            // 交换位置
            exchange(index, 2 * index + 1);
            // 做下潜操作
            down(2 * index + 1);
        } else if (2 * index + 2 < size && array[index].priority() < array[2 * index + 2].priority()) {
            // 交换位置
            exchange(index, 2 * index + 2);
            // 做下潜操作
            down(2 * index + 2);
        }
    }
​
    /**
     * 交换索引 i 和索引 j
     *
     * @param i
     * @param j
     */
    private void exchange(int i, int j) {
        E temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
​
​
    /**
     * 返回优先级最高的一个数据
     *
     * @return
     */
    @SuppressWarnings("all")
    @Override
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return (E) array[0];
    }
​
    @Override
    public boolean isEmpty() {
        return size == 0;
    }
​
    @Override
    public boolean isFull() {
        return size == array.length;
    }
​
    @Override
    public int size() {
        return size;
    }
    
}

你可能感兴趣的:(数据结构和算法学习,java,算法,数据结构)