数据结构-堆

概述

堆是一个完全二叉树,子节点比父节点小的堆是大顶堆,子节点比父节点大的是小丁堆,对利用堆结构可以轻松的实现排序以及类似优先级队列这种更高级的数据结构。

由于堆是一个完全二叉树,所以可以很方便的使用数组进行存储,当添加数据时,将其添加到末尾,然后与其父节点比较大下和调换位置,直到满足堆的性质,当移除堆顶元素时,将最后一个节点移动到根节点的位置,然后与他的子节点进行比较并交换位置,直到满足堆的性质。

代码实现

public class MaxHeap<E extends Comparable<E>> {
    private List<E> data;
    //private int size;

    public MaxHeap(int capacity) {
        //底层用一个动态数组来存储数据
        data = new ArrayList<>(capacity);
    }

    //初始化时,给定数据,自动构建一个大顶堆
    public MaxHeap(E[] dataArray) {
        data = new ArrayList<E>(Arrays.asList(dataArray));
        if (dataArray.length > 1) {
            //从最后一个父节点开始,指向shiftDown操作
            for (int i = parent(data.size() - 1);i >= 0;i --) {
                siftDown(i);
            }
        }
    }

    public void add(E e){
        //添加到末尾
        data.add(e);
        //执行上浮操作
        siftUp(data.size() - 1);
    }
    
    public E findMax() {
        if (isEmpty()) {
            throw new InternalException("heap is empty.");
        }
        //返回堆顶数据
        return data.get(0);
    }

    public E extractMax() {
        E ret = findMax();
        E last = data.get(getSize() - 1);
        data.set(0, last);
        data.remove(getSize() - 1);
        siftDown(0);
        return ret;
    }


    /**
     * 上浮:添加数据时,先将数据添加到最后一个节点,然后在执行上浮操作
     *      直到整体满足堆的性质
     *  过程:如果父节点的值比当前节点小,就将父节点和当前节点调换位置,直到根节点为止
     * */
    private void siftUp(int idx) {
        while (idx > 0 && data.get(parent(idx)).compareTo(data.get(idx)) < 0) {
            //交换父子节点的位置
            E temp = data.get(parent(idx));
            data.set(parent(idx), data.get(idx));
            data.set(idx, temp);

            //向上移动指针
            idx = parent(idx);
        }
    }

   /**
     * 下沉:移除头节点时,将最后一个节点移动到头节点,然后执行下沉操作,
     *      直到整体满足堆的性质
     *
     * */
    private void siftDown(int idx) {
        //卡以下左节点的边界
        while (left(idx) < data.size()) {
            int j = left(idx);
            //根节点要跟最大的子节点进行位置交换,所以要先找出来最大的是左子节点还是右子节点
            //如果存在右节点,并且右节点的值比左节点的值大,就将j指向右节点,
            //即找到左右子节点中较大的那个
            if (j + 1 < data.size()
                    && data.get(j + 1).compareTo(data.get(j)) > 0) {
                j ++;
            }

            //如果idx处的值比子节点中最大的值还大,就可以终止了
            if (data.get(idx).compareTo(data.get(j)) > 0) {
                break;
            }

            //否则,就交换位置
            E temp = data.get(idx);
            data.set(idx, data.get(j));
            data.set(j, temp);

            //相下一层推进指针
            idx = j;
        }
    }

    //定义计算索引的公式
    private int parent(int i) {
        return (i - 1) / 2;
    }
    private int left(int i) {
        return 2 * i + 1;
    }
    private int right(int i) {
        return 2 * i + 2;
    }
    
    //工具方法
    public int getSize() {
        return data.size();
    }
    public boolean isEmpty() {
        return data.isEmpty();
    }

    public static void main(String[] args) {
        MaxHeap<Integer> heap = new MaxHeap<>(10);
        heap.add(4);
        heap.add(100);
        heap.add(45);
        heap.add(34);
        heap.add(888);
        System.out.println(heap.findMax());
        System.out.println("----");
        for (int i = 0;i < 5;i ++) {
            System.out.println(heap.extractMax());
        }
    }
}

利用堆实现优先级队列

/**
 * 利用大顶堆实现一个优先级队列
 * */
public class EPriorityQueue<E extends Comparable<E>> {

    private MaxHeap<E> maxHeap;

    public EPriorityQueue() {
        maxHeap = new MaxHeap<>(16);
    }

    //入队
    public void enqueue(E e) {
        maxHeap.add(e);
    }
    //出队
    public E enqueue() {
        return maxHeap.extractMax();
    }

    //获取队头元素
    public E getFront() {
        return maxHeap.findMax();
    }

    public int getSize() {
        return maxHeap.getSize();
    }

    public boolean isEmpty() {
        return maxHeap.isEmpty();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

echo20222022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值