小顶堆

316 阅读1分钟

小顶堆

小顶堆,用于最小优先队列的底层,且不进行多个堆合并的情况。常用于事件系统,关键字主要使用的是时间戳。

// 小顶堆的实现
public class Heap {
    private long[] elements;
    private int size = 0;
    private int capacity = 8;

    public Heap() {
        elements = new long[capacity];
    }

    // 扩容
    private void enrich() {
        capacity = capacity + (capacity >> 1);
        long[] temp = new long[capacity];
        System.arraycopy(elements,0,temp,0,elements.length);
        elements = temp;
    }

    // 添加一个元素
    public void add(long data) {
        // 容量不够,扩容为原来的3/2
        if (size >= capacity) {
            enrich();
        }
        int i = size;
        elements[i] = data;
        while (i>0 && elements[i]<=elements[(i-1)/2]) {
            long temp = elements[i];
            elements[i] = elements[(i-1)/2];
            elements[(i-1)/2] = temp;
            i = (i-1)/2;
        }
        size ++;
    }

    // 删除一个最小值并取出
    public long rm() {
        if (size == 0) {
            throw new RuntimeException("堆为空,无法删除");
        }
        long result = elements[0];
        elements[0] = elements[size-1];
        elements[size-1] = result;
        int i = 0;
        while (i < (size-2)>>1) {
            if (elements[2*i+1] <= elements[2*i+2]){
                long temp1 = elements[2*i+1];
                elements[2*i+1] = elements[i];
                elements[i] = temp1;
                i = 2*i+1;
            } else {
                long temp1 = elements[2*i+2];
                elements[2*i+2] = elements[i];
                elements[i] = temp1;
                i = 2*i+2;
            }
        }
        size --;
        return result;
    }

    // 遍历堆数据结构
    public void traverse() {
        System.out.printf("[");
        if (size != 0) {
            Set<Integer> set = new HashSet<>();
            System.out.printf("%d,", elements[0]);
            set.add(0);
            // 所有节点的左右孩子都遍历到才结束
            while (set.size() != size){
                // 从根节点开始
                // 对所有已访问节点的左右孩子进行判断,
                // 按层次进行扩散,
                // 典型的dfs算法,
                // 算法类似编译原理里NFA对字符串的判别方式
                long min = Long.MAX_VALUE;
                int minindex = -1;
                for (Integer index : set) {
                    int left = index*2+1;
                    if (left < size && !set.contains(left) && elements[left]<min) {
                        min = elements[left];
                        minindex = left;
                    }
                    int right = index*2+2;
                    if (right < size && !set.contains(right) && elements[right]<min) {
                        min = elements[right];
                        minindex = right;
                    }
                }
                if (minindex != -1) {
                    System.out.printf("%d,", elements[minindex]);
                    set.add(minindex);
                }
            }
        }
        System.out.println("]");
    }

    // 遍历数组
    public void all() {
        for (int i = 0; i < size; i++) {
            System.out.printf("%d,", elements[i]);
        }
        System.out.println();
    }
}

// 测试
public class Main {
    public static void main(String[] args) {
        Heap heap = new Heap();
        heap.add(0L);
        heap.add(4L);
        heap.add(3L);
        heap.add(1L);
        heap.add(2L);
        heap.add(5L);
        heap.add(8L);
        heap.add(7L);
        heap.add(6L);
        heap.add(9L);
        heap.all();
        heap.traverse();
    }
}