Java - TaskQueue 最小堆算法

339 阅读1分钟

import java.util.Arrays; import java.util.TimerTask;

class TaskQueue {

/**
 * 使用数组存为,数据是TimerTask类型
 */
private TimerTask[] queue = new TimerTask[128];

/**
 * 任务队列的大小,queue[0]没有使用,queue[1]-queue[size]是排好序的
 */
private int size = 0;

int size() {
    return size;
}

/**
 * 
 */
TimerTask getMin() {
    return queue[1];
}

TimerTask get(int i) {
    return queue[i];
}



/**
 * 这里快速删除,并没有维护堆,可能是大神考虑到只有在TimerTask的purge中调用了改方法
 * purge方法中如果删除了至少1个元素,就会调用heapify方法重新建堆
 * 1 <= i <= size.
 */
void quickRemove(int i) {
    assert i <= size;

    queue[i] = queue[size];
    queue[size--] = null;  // 删除多余的引用,防止内存泄露
}


boolean isEmpty() {
    return size==0;
}

/**
 * 清除队列
 */
void clear() {
    // 清除引用,避免内存泄露
    for (int i=1; i<=size; i++)
        queue[i] = null;

    size = 0;
}

/**
 * 添加一个新任务到队列作为叶子节点,然后从这个节点开始重新建堆
 */
void add(TimerTask task) {
    // 保证数组的大小能够存放
    if (size + 1 == queue.length)
        queue = Arrays.copyOf(queue, 2*queue.length);

    queue[++size] = task;
    fixUp(size);
}


/**
 * 自底向上重建堆,k>>1 整数k右移1位表示除以2 k>>1 ==>k/2
 * 根据堆的性质可以知道一个节点k 的父节点可以表示为k/2
 * 因此这个方法的从指定的节点k,依次检查和它的父节点的关系
 * 如果父节点大于子节点就交换父节点与子节点(小顶堆,堆顶是最小的元素)
 */
private void fixUp(int k) {
    while (k > 1) {
        int j = k >> 1;
        if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
            break;
        TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
        k = j;
    }
}


/**
 *自顶向下重建堆,k<<1 整数k向左移1位表示乘以2 k<<1 ==>k*2
 *根据堆的性质知道,对于一个节点k,它的子节点分别为k*2 和k*2+1(如果存在)
 *
 *这个方法就是对于节点k如果存在子节点,则找到子节点中较小的一个和k进行比较交换
 */
private void fixDown(int k) {
    int j;
    while ((j = k << 1) <= size && j > 0) {
        if (j < size &&
            queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
            j++; // j indexes smallest kid
        if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
            break;
        TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
        k = j;
    }
}

/**
 * 移除最小的(第一个),然后把最后一个元素移到堆顶,执行自顶向下重新建堆
 */
void removeMin() {
    queue[1] = queue[size];
    queue[size--] = null; 
    fixDown(1);
}

/**
 * 最小元素改变,重新建堆
 */
void rescheduleMin(long newTime) {
    queue[1].nextExecutionTime = newTime;
    fixDown(1);
}

/**
 * 重建堆,因为叶子节点满足条件了,所以从最后一个根节点开始
 */
void heapify() {
    for (int i = size/2; i >= 1; i--)
        fixDown(i);
}

}