java数据结构--优先级队列

一.概念

 

优先级队列是一种特殊类型的队列,它根据每个元素的优先级进行排序和访问。较高优先级的元素将在较低优先级的元素之前被处理。

优先级队列可以使用不同的数据结构实现,包括数组、链表或二叉堆。其中,二叉堆是实现优先级队列的常见选择。二叉堆是一个完全二叉树,具有以下特性:

  1. 每个节点的值都大于或等于其子节点的值(最大堆),或者每个节点的值都小于或等于其子节点的值(最小堆)。
  2. 二叉堆是一个完全二叉树,意味着除最后一层外,每一层都是满的,并且最后一层的节点都尽可能地靠左排列。

优先级队列的主要操作包括:

  1. 插入(add):将一个元素插入到队列中,并根据其优先级进行排序。
  2. 删除最高优先级元素(poll):从队列中删除具有最高优先级的元素。
  3. 查看最高优先级元素(peek):查看具有最高优先级的元素,而不删除它。

优先级队列在各个领域中的应用非常广泛,例如任务调度、数据压缩、网络路由等

二.队列Queue接口

 我们先定义好Queue接口,然后实现它

public interface Queue {



    /**
     * 向队列尾添加元素
     * @return 添加成功返回true,添加失败返回false
     */
     boolean add(E e);

    /**
     * 从队列头删除一个节点,并取出该元素
     * @return 队头元素
     */
    E pull();

    /**
     * 从队列头取出该元素,但是不删除该节点
     * @return 队头元素
     */
    E peek();

    /**
     * 判断队列是否为空
     * @return 空为true,非空为false
     */
    boolean isEmpty();

    /**
     * 判断队列是否为满
     * @return 满为true,不满为false
     */
    boolean isFull();

}

三.优先级接口Proiroity,任务类Task

我们定义优先级接口Proiroity,它提供一个proiority()方法,获取元素的优先级

public interface Priority {

    //返回优先级
    int priority();

}

我们定义任务类Task,作为放入优先级队列中的元素

/**
 * 模拟任务类,有优先级的任务
 */
public class Task implements Priority{

    String name;

    int priority;

    public Task(String name,int priority){
        this.name = name;
        this.priority = priority;
    }



    @Override
    public int priority() {
        return this.priority;
    }

    @Override
    public String toString() {
        return "Task{" +
                "name='" + name + '\'' +
                ", priority=" + priority +
                '}';
    }
}

四.无序数组实现优先级队列

 

/**
 * 无序数组实现优先级队列
 * @param 
 */
public class DisOrderArrayPriorityQueue implements Queue,Iterable {

    //数组
    E[] array;
    //元素的个数,也可以作为尾指针
    int size;

    @SuppressWarnings("all")
    public DisOrderArrayPriorityQueue(int capacity){
        array = (E[]) new Priority[capacity];
        size = 0;
    }

    /**
     * 返回优先级最大的索引位置
     * @return
     */
     private int maxPriority(){
        int max = 0;
         for (int i = 0; i < size; i++) {
             if(array[max].priority() < array[i].priority()){
                 //将max索引设置为i
                 max = i;
             }
         }
         return max;
     }

    /**
     * 移除优先级最大的元素
     *
     *            m
     *  0 1 2 3 4 5 6
     *  a b c d e f g
     *
     *  index=5,
     *
     * @param index
     */
     private void remove(int index){
         if(index < size){
             //当该索引不是最后一个位置时需要移动数组
             System.arraycopy(array,index+1,array,index,size-1-index);
         }
         //否则不需要移动数组
         //都需要让size--;
         size--;
     }

    /**
     * 添加元素,直接在末尾添加就可以了
     * @param e
     * @return
     */
    @Override
    public boolean add(E e) {
       //先判断是否满
        if (isFull()) {
            return false;
        }
        array[size] = e;
        size++;
        return true;
    }

    /**
     * 移除优先级最大的元素
     * @return
     */
    @Override
    public E pull() {
        if(isEmpty()){
            return null;
        }
        int max = maxPriority();
        E e = array[max];
        remove(max);
        return e;
    }

    /**
     * 返回优先级最大的元素
     * @return
     */
    @Override
    public E peek() {
        if(isEmpty()){
            return null;
        }
        int max = maxPriority();
        return array[max];
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean isFull() {
        return size == array.length;
    }

    //遍历
    @Override
    public Iterator iterator() {
        return new Iterator() {

            int p = 0;

            @Override
            public boolean hasNext() {
                return p != size;
            }

            @Override
            public E next() {
                E e = array[p];
                p++;
                return e;
            }
        };
    }
}

五.有序数组实现优先级队列

/**
 * 有序数组实现优先级队列
 * @param 
 */
public class OrderArrayPriorityQueue implements Queue ,Iterable{

    E[] array;

    int size;

    @SuppressWarnings("all")
    public OrderArrayPriorityQueue(int capacity){
        array = (E[]) new Priority[capacity];
        size = 0;
    }


    private void insertTo(E e){
        int i = size - 1;

        while ( i >= 0 && array[i].priority() > e.priority() ){
             //将i后移
            array[i+1] = array[i];
            i--;
        }
        //插入到正确的位置
        array[i+1] = e;
    }




    @Override
    public boolean add(E e) {
        if(isFull()){
            return false;
        }
        //先插入到正确的索引位置
        insertTo(e);
        size++;
        return true;
    }

    /**
     * 移除优先级最高的元素(最后一个元素)
     * @return
     */
    @Override
    public E pull() {
        if(isEmpty()){
            return null;
        }
        E e = array[size-1];
        //让size--
        size--;
        array[size] = null; //help GC
        return e;
    }

    /**
     * 返回队列优先级最高的元素(最后一个元素)
     * @return
     */
    @Override
    public E peek() {
       if(isEmpty()){
           return null;
       }
       return array[size-1];
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean isFull() {
        return size == array.length;
    }


    //遍历
    @Override
    public Iterator iterator() {
        return new Iterator() {

            int p = 0;

            @Override
            public boolean hasNext() {
                return p != size;
            }

            @Override
            public E next() {
                E e = array[p];
                p++;
                return e;
            }
        };
    }



}

六.无序数组和有序数组的区别

对于无序数组来说,每次添加元素是直接添加在数组的末尾,添加起来方便,但是取出优先级最高的元素需要进行一次排序,也就是取出复杂;

对于有序数组来说,每次取出优先级最高的元素就是取去数组末尾的元素,但是添加元素时,需要进行一次插入排序,找到合适的位置,也就是添加复杂

七.大顶堆实现优先级队列

1.大顶堆

 java数据结构--优先级队列_第1张图片

2.基于数组的实现

 堆虽然是非线性的数据结构,但是我们可以利用数组来存储他的节点,这是有规律的,

  节点i的父节点是floor((i-1)/2)

 节点i的左子节点是2i + 1, 右子节点是2i + 2

下面我们用数组来实现堆的优先级队列

/**
 * 数组实现大顶堆,实现优先级队列
 * @param 
 */
public class HeapArrayPriorityQueue implements Queue  {


    E[] array;

    int size;

    @SuppressWarnings("all")
    public HeapArrayPriorityQueue(int capacity){
        array = (E[]) new Priority[capacity];
        size = 0;
    }


    /**
     * 添加元素
     *
     * 1.首先将元素添加到数组的末尾
     * 2.然后让size++;
     * 3.然后调整堆使其符合大顶堆
     *
     *
     * @param e
     * @return
     */
    @Override
    public boolean add(E e) {
       if(isFull()){
           return false;
       }
       //先记录child的索引位置,就是在数组的末尾添加
        int child = size++;
       //找到他的父节点的索引位置
        int parent = (child - 1)/2;
        //然后比较要添加元素的优先级和parent的优先级
        //而且child必须是大于0的,当child==0时,表示到堆顶了,就不需要再调整了
        while ( child > 0 && e.priority() > array[parent].priority() ){
            //当e的优先级大于parent的优先级时
            //parent要向下沉到child处
            array[child] = array[parent];
            //然后将child的索引位置设置为parent
            child = parent;
            //继续找到child的父节点位置
            parent = (child - 1)/2;
        }
        //调整完成后,child处就是要插入的正确位置
        array[child] = e;

        return true;
    }

    @Override
    public E pull() {
        if(isEmpty()){
            return null;
        }
        //先交换0处和最后一个位置
        swap(0,size-1);
        size--;
        E e = array[size];
        array[size] = null; //help GC

        //调整堆,也就是让堆顶元素下沉到合适的位置
        adjust(0);

        return e;
    }

    @Override
    public E peek() {
       if(isEmpty()){
           return null;
       }
       return array[0];
    }

    /**
     * 交换数组中两个索引位置的值
     * @param i
     * @param j
     */
    private void swap(int i ,int j){
        E e  = array[i];
        array[i] = array[j];
        array[j] = e;
    }

    /**
     * 调整堆,从堆顶开始
     * @param parent
     */
    private void adjust(int parent){
       int left = parent * 2 + 1;
       int right = left + 1;
       //假设parent最大
        int max = parent;
        //分别判断左孩子,右孩子与parent的优先级
        if(left > 0 && array[left].priority() > array[parent].priority()){
            max = left;
        }
        if(right > 0 && array[right].priority() > array[parent].priority()){
            max = right;
        }
        //如果max!=parent,就需要交换max和parent
        if(max != parent){
            swap(max,parent);
            //递归调用,将max作为parent继续调用
            adjust(max);
        }
    }



    @Override
    public boolean isEmpty() {
        return  size == 0;
    }

    @Override
    public boolean isFull() {
        return size == array.length;
    }
}

你可能感兴趣的:(java数据结构,java,数据结构)