数据结构与算法——Java实现 29.优先级队列

事缓则圆,人缓则安

                        —— 24.10.5

一、优先级队列

优先级队列                一端进,另一端出                按优先级出队

普通队列                    一端进,另一端出                先进先出FIFO


二、无序数组实现

Type parameters:<E>:队列中元素的类型,必须实现Priority接口


思路

设置对象优先级Entry,每个存入数组的元素都有它的优先级,无序数组实现时,加入元素直接在数组尾部进行添加,删除元素时遍历一遍数组,寻找优先级最大的元素,将优先级最大的元素进行删除


对象优先级接口

public interface Priority {
    // 返回对象的优先级,约定数字越大,优先级越高
    // Returns:优先级
    int priority();
}

优先级接口实现

public class Entry implements Priority{
    String value;
    int priority;

    public Entry(String value, int priority) {
        this.value = value;
        this.priority = priority;
    }

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

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

队列接口

public interface Queue<E> {
    /*
        向队列尾部插入值
        Params:value-待插入值
        Returns:插入成功返回true,插入失败返回false
     */
    boolean offer(E e);

    /*
        从队列头获取值,并移除
        Returns:如果队列非空返回头部值,否则返回null
     */
    E poll();

    /*
        从队列头获取值,不移除
        Returns:如果队列非空返回队头值,否则返回null
     */
    E peek();

    /*
        检查队列是否为空
        Return:空返回true,否则返回false
     */
    boolean isEmpty();

    /*
        检查队列是否为满
        Return:满返回true,否则返回false
     */
    boolean isFull();
}

无序数组实现队列接口

import Day10Queue.Queue;

import java.util.Arrays;

public class PriorityQueue1<E extends Priority> implements Queue<E> {
    // 数据类型E要实现Priority接口,所以数组类型直接定义成Priority
    Priority[] array;
    int size;

    public PriorityQueue1(int capacity) {
        array = new Priority[capacity];
    }

    // 添加元素
    @Override
    public boolean offer(E e) {
        if (isFull()) {
            return false;
        }
        array[size] = e;
        size++;
        return true;
    }

    // 寻找优先级最高的索引值
    private int selectMax(){
        int max = 0;
        for (int i = 0; i < size - 1; i++) {
            // 寻找优先级最高的元素
            if (array[i].priority() > array[max].priority()){
                max = i;
            }
        }
        return max;
    }
    
    // 删除元素
    @Override
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        int max = selectMax();
        E e = (E) array[max];
        remove(max);
        return e;
    }

    // 删除元素
    private void remove(int index) {
        if (index == size - 1) {
            array[size] = null;
        } else if (index < size-1) {
            // 原数组 原数组起始位置 目的数组 目的数组起始位置 移动的元素个数
            System.arraycopy(array,index+1,array,index,size-index-1);
        }
        size--;
        array[size] = null;
    }

    // 获取元素
    @Override
    public E peek() {
        if (isEmpty()){
            return null;
        }
        int max = selectMax();
        return (E) array[max];
    }

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

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

    @Override
    public String toString() {
        return "PriorityQueue1{" +
                "array=" + Arrays.toString(array) +
                ", size=" + size +
                '}';
    }
}

测试类

import org.junit.Test;

import static org.junit.jupiter.api.Assertions.*;

public class TestPriorityQueue1 {

    @Test
    public void poll(){
        PriorityQueue1<Entry> queue1 = new PriorityQueue1<>(5);
        queue1.offer(new Entry("task1",4));
        queue1.offer(new Entry("task2",3));
        queue1.offer(new Entry("task3",2));
        queue1.offer(new Entry("task4",5));
        queue1.offer(new Entry("task5",1));
        assertFalse(queue1.offer(new Entry("task6",7)));

        assertEquals(5, queue1.poll().priority());
        assertEquals(4, queue1.poll().priority());
        assertEquals(3, queue1.poll().priority());
        assertEquals(2, queue1.poll().priority());
        assertEquals(1, queue1.poll().priority());
        System.out.println(queue1.toString());
    }
}


三、有序数组实现

Type parameters:<E>:队列中元素的类型,必须实现Priority接口


思路

将元素按照优先级进行排序存储在基于有序数组实现的队列中,删除元素时只需要在数组最尾端进行出队操作,添加元素时需要遍历一遍队列内的元素,寻找新元素优先级存储的位置,将新元素存储在其优先级按序排列的数组中的位置


 对象优先级接口

public interface Priority {
    // 返回对象的优先级,约定数字越大,优先级越高
    // Returns:优先级
    int priority();
}

优先级接口实现

public class Entry implements Priority{
    String value;
    int priority;

    public Entry(String value, int priority) {
        this.value = value;
        this.priority = priority;
    }

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

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

队列接口

public interface Queue<E> {
    /*
        向队列尾部插入值
        Params:value-待插入值
        Returns:插入成功返回true,插入失败返回false
     */
    boolean offer(E e);

    /*
        从队列头获取值,并移除
        Returns:如果队列非空返回头部值,否则返回null
     */
    E poll();

    /*
        从队列头获取值,不移除
        Returns:如果队列非空返回队头值,否则返回null
     */
    E peek();

    /*
        检查队列是否为空
        Return:空返回true,否则返回false
     */
    boolean isEmpty();

    /*
        检查队列是否为满
        Return:满返回true,否则返回false
     */
    boolean isFull();
}

有序数组实现队列接口

import Day10Queue.Queue;

public class PriorityQueue2<E extends Priority> implements Queue<E> {
    Priority[] array;
    int size;

    public PriorityQueue2(int capacity) {
        array = new Priority[capacity];
    }

    @Override
    public boolean offer(E e) {
        if (isFull()){
            return false;
        }
        insert(e);
        size++;
        return false;
    }

    private void insert(E e) {
        int i = size-1;
        while (i >= 0 && array[i].priority() > e.priority()){
            array[i+1] = array[i];
            i--;
        }
        array[i+1] = e;
    }

    @Override
    public E poll() {
        if (isEmpty()){
            return null;
        }
        E e = (E) array[size-1];
        array[size-1] = null;
        size--;
        return e;
    }

    @Override
    public E peek() {
        if (isEmpty()){
            return null;
        }
        return (E) array[array.length - 1];
    }

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

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

测试类

import org.junit.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;

public class TestPriorityQueue2 {
    @Test
    public void poll(){
        PriorityQueue2<Entry> queue1 = new PriorityQueue2<>(5);
        queue1.offer(new Entry("task1",4));
        queue1.offer(new Entry("task2",3));
        queue1.offer(new Entry("task3",2));
        queue1.offer(new Entry("task4",5));
        queue1.offer(new Entry("task5",1));
        assertFalse(queue1.offer(new Entry("task6",7)));

        assertEquals(5, queue1.poll().priority());
        assertEquals(4, queue1.poll().priority());
        assertEquals(3, queue1.poll().priority());
        assertEquals(2, queue1.poll().priority());
        assertEquals(1, queue1.poll().priority());
        System.out.println(queue1.toString());
    }
}

四、堆实现

数据结构——堆

定义

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

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

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

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

完全二叉树:除叶子节点那一层(最后一层)外,其余层都被填满(最后一层可填满可不填满)

特性:完全二叉树添加元素必须从左到右依次填满

特征

如果从索引0开始存储节点数据

        节点 i 的父节点为 floor((i-1)/2),当i>0时

        节点 i 的左子节点为 2i+ 1,右子节点为 2i + 2,当然它们需要 < size

如果从索引1开始存储节点数据

        节点 i 的父节点为 floor(i/2),当i>1时

        节点 i 的左子节点为 2i,右子节点为 2i + 1 ,同样需要 < size


大顶堆实现优先级队列

思路

根据大顶堆特性,root根节点就是优先级最高的元素,索引0位置就是要出队的元素,直接在队头出队进行删除

入堆新元素,加入到数组末尾(索引位置child)

不断比较新加入元素与它父亲节点(parrent)的优先级

        如果父节点优先级低,则向下移动,并找到下一个parrent

        直至父节点优先级更高或 child == 0 为止

交换堆顶和尾部元素,让尾部元素出队

(下潜)

        从堆顶开始,将父元素与两个孩子较大者交换

        直到父元素大于两个孩子,或没有孩子为止


元素优先级接口

public interface Priority {
    // 返回对象的优先级,约定数字越大,优先级越高
    // Returns:优先级
    int priority();
}

队列接口

import Day13PriorityQueue.Priority;

public interface Queue<E> {
    /*
        向队列尾部插入值
        Params:value-待插入值
        Returns:插入成功返回true,插入失败返回false
     */
    boolean offer(E e);

    /*
            从队列头获取值,并移除
            Returns:如果队列非空返回头部值,否则返回null
         */
    E poll();

    /*
        从队列头获取值,不移除
        Returns:如果队列非空返回队头值,否则返回null
     */
    E peek();

    /*
        检查队列是否为空
        Return:空返回true,否则返回false
     */
    boolean isEmpty();

    /*
        检查队列是否为满
        Return:满返回true,否则返回false
     */
    boolean isFull();
}

优先级队列的实现

import Day10Queue.Queue;

@SuppressWarnings("all")
public class PriorityQueue3<E extends Priority> implements Queue<E> {
    Priority[] array;
    int size;

    public PriorityQueue3(int capacity) {
        array = (Priority[]) new Priority[capacity];
    }

    @Override
    public boolean offer(E e) {
        /*
            入堆新元素,加入到数组末尾(索引位置child)
            不断比较新加入元素与它父亲节点(parrent)的优先级
            如果父节点优先级低,则向下移动,并找到下一个parrent
            直至父节点优先级更高或 child == 0 为止
         */
        if (isFull()){
            return false;
        }
        int child = size;
        size++;
        int parent = (child - 1) / 2;
        while (child >0 && e.priority() > array[parent].priority()) {
            array[child] = array[parent];
            child = parent;
            parent = (child - 1) / 2;
        }
        array[child] = e;
        return true;
    }

    @Override
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        swap(0,size-1);
        size--;
        Priority e = array[size];
        array[size] = null;

        // 下潜
        down(0);
        return (E) e;
    }

    private void down(int parent) {
        int left = 2 * parent+1;
        int right = left + 1;
        // 假设父元素优先级最高
        int max = parent;
        if (left < size && array[left].priority() > array[max].priority()) {
            max = left;
        }
        if (right < size && array[right].priority() > array[max].priority()) {
            max = right;
        }
        // 有孩子优先级大于父节点
        if (max != parent) {
            swap(max,parent);
            down(max);
        }
    }

    private void swap(int i, int j) {
        Priority temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    @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;
    }
}

 测试类

import org.junit.Test;
import static org.junit.jupiter.api.Assertions.*;

public class TestPriorityQueue3 {
    @Test
    public void test() {
        PriorityQueue3<Entry> queue = new PriorityQueue3<>(5);
        queue.offer(new Entry("task1",4));
        queue.offer(new Entry("task2",3));
        queue.offer(new Entry("task3",2));
        queue.offer(new Entry("task4",1));
        queue.offer(new Entry("task5",5));
        assertFalse(queue.offer(new Entry("task6",6)));

        assertEquals(5,queue.poll().priority());
        assertEquals(4,queue.poll().priority());
        assertEquals(3,queue.poll().priority());
        assertEquals(2,queue.poll().priority());
        assertEquals(1,queue.poll().priority());
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值