实现游戏陪玩app源码的优先级队列没有那么困难

前言

游戏陪玩app源码事件系统中有很多的事件,每个事件都定义了不同的权重值,系统需要优先处理权重较高的事件,这里你就需要使用到优先级队列,本篇我们一起来学习游戏陪玩app源码实现优先级队列的常用方式

队列API定义

在实现游戏陪玩app源码优先级队列之前,我们需要先定义出优先级队的API,优先级队列是一种抽象的数据结构,我们依然可以基于前面我们使用到的队列API来修改。

public interface Queue<T> extends Iterable<T> {
    void enqueue(T item); //入队列

    T dequeue(); //出队列

    int size();

    boolean isEmpty();
}

其中的入队列enqueue和出队列dequeue是我们主要需要实现的方式,也是优先级队列的核心方法。

初级版本的实现

队列API的抽象类

public abstract class AbstractQueue<T> implements Queue<T> {
    private Comparator<T> comparator;

    public AbstractQueue(Comparator<T> comparator) {
        this.comparator = comparator;
    }

    public boolean less(T a, T b) {
        return comparator.compare(a, b) < 0;
    }

    public void exch(T[] array, int i, int j) {
        T tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }
}

基于无序数组实现
实现游戏陪玩app源码优先级队列的最简单实现实现方式,enqueue和栈的push方式实现方式一致,dequeue可以参考选择排序的实现,循环一遍数组,找出最大值和数组最后一个元素交换,然后删除它;

public class DisorderPriorityQueue<T> extends AbstractQueue<T> {

    private T[] queue;
    private int size;

    public DisorderPriorityQueue(int max, Comparator<T> comparator) {
        super(comparator);
        this.queue = (T[]) new Object[max];
    }

    @Override
    public void enqueue(T item) {
        queue[size++] = item;
    }

    @Override
    public T dequeue() {
        int index = 0;
        for (int i = 1; i < size; i++) {
            if (less(queue[index], queue[i])) {
                index = i;
            }
        }
        size--;
        exch(queue, index, size);
        T data = queue[size];
        queue[size] = null;
        return data;
    }
    //省略其他函数
}

这里只实现了游戏陪玩app源码定长的优先级队列,如何实现自动扩容呢?基于无序数组实现的enqueue时间复杂度是O(1),dequeue时间复杂度是O(n)

基于有序数组实现
基于有序数组实现就是在入队的时候保证游戏陪玩app源码数组有序,那么在出队列的时候可以直接删掉最大值;插入的过程和插入排序类似的操作

public class OrderPriorityQueue<T> extends AbstractQueue<T> {

    private T[] queue;
    private int size;

    public OrderPriorityQueue(int max, Comparator<T> comparator) {
        super(comparator);
        this.queue = (T[]) new Object[max];
    }

    @Override
    public void enqueue(T item) {
        queue[size++] = item;
        for (int i = size - 1; i > 1 && less(queue[i], queue[i - 1]); i--) {
            exch(queue, i, i - 1);
        }
    }

    @Override
    public T dequeue() {
        size--;
        T data = queue[size];
        queue[size] = null;
        return data;
    }

    //省略其他函数
}

enqueue时间复杂度是O(n),dequeue时间复杂度是O(1)

基于链表实现
基于链表的实现与上面的类似,有兴趣的可以自己实现

二叉堆实现

二叉堆的定义
在二叉堆中,游戏陪玩app源码每个节点都将大于等于它的子节点,也成为堆有序;其中根节点是最大的节点。

二叉堆的表示:

在这里插入图片描述

重点:
在一个二叉堆中,位置k节点的父节点的位置为k/2,它的两个子节点的位置为2k和2k+1; 基于这点,我们可以用游戏陪玩app源码数组来表示二叉堆,通过移动数组的下标来找到节点父节点和子节点

在游戏陪玩app源码元素进行插入和删除操作的过程中,会破坏堆有序,所以我们需要做一些操作来保证堆再次有序;主要有两种情况,当某个节点的优先级上升,我们需要由下向上恢复堆有序(下沉);当某个节点优先级下降,我们需要由上向下恢复堆有序(上浮)

由上向下恢复堆有序(上浮)

private void swim(int k) {
    while (k > 0 && less(queue[k / 2], queue[k])) {
        exch(queue, k / 2, k);
        k = k / 2;
    }
}

根据游戏陪玩app源码当前的节点k找到父节点的位置k/2,比较当前节点和父节点,如果比父节点大就交换,直到找个比当前节点大的父节点或者已上浮到了根节点

由下向上恢复堆有序(下沉)

private void sink(int k) {
    while (2 * k <= size) {
        int i = 2 * k;
        if (less(queue[i], queue[i + 1])) {
            i++;
        }
        if (less(queue[i], queue[k])) {
            break;
        }
        exch(queue, i, k);
        k = i;
    }
}

二叉堆实现优先级队列

  • 入队操作:将游戏陪玩app源码新的元素添加到数组末尾,让新元素上浮到适合位置,增加堆的大小
  • 出队操作:将游戏陪玩app源码最大的根节点删除,然后把最后一个元素放入到顶端,下层顶端元素到合适位置,减小堆大小
public class BinaryHeapPriorityQueue<T> extends AbstractQueue<T> {
    private T[] queue;
    private int size;

    public BinaryHeapPriorityQueue(int max, Comparator<T> comparator) {
        super(comparator);
        this.queue = (T[]) new Object[max + 1];
    }

    @Override
    public void enqueue(T item) {
        this.queue[++size] = item;
        this.swim(size);
    }

    @Override
    public T dequeue() {
        T max = this.queue[1];
        exch(this.queue, 1, size--);
        this.queue[size + 1] = null; //释放内存
        this.sink(1);
        return max;
    }
    
     //省略其他函数
}

注意:
由于我们为了方便计算父节点和子节点的索引位置,所以游戏陪玩app源码数组中的第一个位置是不会使用的;可以自己思考下使用第一个位置,那么子节点和父节点的位置应该如何计算?

  • 基于堆的实现,入队和出队的时间复杂对都是logN,解决了初级版本实现的问题。
  • 数组大小动态扩容和缩容依然可以参考之前栈的实现方式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值