数据结构学习,堆(Heap)和优先队列(PriorityQueue)(java语言)

数据结构学习,堆(Heap)和优先队列(PriorityQueue)(java语言)

  • 1.堆(Heap)
    • 1.1向堆中添加元素
    • 1.2查看堆中的最大元素
    • 1.3取出堆中最大元素
    • 1.4Replace 和 Heapify
  • 2.优先队列(PriorityQueue)
  • 3.总结

1.堆(Heap)

二叉堆是一颗完全二叉树,其满足以下性质:

  • 1.父节点的值大于子节点的值
  • 2.对于除了根节点以外的每个节点的父节点都为(index - 1)/2(底层是一个0-n的数组)
  • 3.对于每个节点来说,他的左孩子索引为2index+1,右孩子为2index+2

对于一个二叉堆的实现,我们底层采用一个动态数组Array的结构。
然后我们先定义一下堆的三个查找索引的操作
(本次试验用的二叉堆为最大堆,java中的优先队列中用的堆为最小堆)

    //返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点索引
    private int parent(int index){
        if(index==0)
            throw new IllegalArgumentException("index- 0 doesn't have parent.");
        return (index-1)/2;
    }
    //返回完全二叉树的数组表示中,,一个节点的左孩子
    private int leftChild(int index){
        return index*2 + 1;
    }
    //返回完全二叉树的数组表示中,,一个节点的右孩子
    private int rightChild(int index){
        return index*2 + 2;
    }

有了这些操作之后,其他的操作都变得很容易

1.1向堆中添加元素

向堆中添加元素就是向数组尾中添加元素,然后使其满足堆的性质。要满足堆的性质,我们就要定义一个siftUp(上浮)操作,将新加入堆的元素与其父节点做对比,如果新加入元素较大,则调用数组中的swap方法将两个元素交换位置,重复此过程,直到新加入元素小于其父节点元素或者已经上浮到了根节点

    private void siftUp(int k){

        while(k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0){
            data.swap(k,parent(k));
            k = parent(k);
        }
    }
    public void add(E e){
        data.addLast(e);
        siftUp(data.getSize() - 1);
    }

1.2查看堆中的最大元素

查看堆中最大元素我们返回数组索引为0的节点就好了

    public E findMax(){
        if(data.getSize()==0){
            throw new IllegalArgumentException("can not findMax when heap is empty");
        }
        return data.get(0);
    }

1.3取出堆中最大元素

首先用findMax函数取出并保存堆中最大的元素,然后将数组中的最后一个元素与第一个元素进行交换(swap),然后移除数组中的最后一个元素,然后对根节点的元素进行siftDown(下沉)操作,即不断与两个子节点中较大的节点比较,如果比较大的节点还要小,就与其交换。

    private void siftDown(int k){
        //判断当前节点是否有左孩子
        while(leftChild(k) < data.getSize()){
            int j = leftChild(k);
            //判断当前节点是否有右孩子且右孩子比左孩子大
            if(j + 1 < data.getSize() && data.get(j + 1).compareTo(data.get(j))>0){
                j = rightChild(k);
            }
            //判断当前节点与左右孩子节点中较大那个的大小比较
            if(data.get(k).compareTo(data.get(j)) >= 0){
                break;
            }
            data.swap(k,j);
            k = j;
        }
    }
    public E extractMax(){
        E ret = findMax();
        data.swap(0,data.getSize() - 1);
        data.removeLast();
        siftDown(0);
        return ret;
    }

1.4Replace 和 Heapify

  • replace
    replace是将新传入节点与最大节点做一个替换的操作,我们可以调用数组中的set方法,将索引为0的元素直接进行修改,然后再将其下沉。
    public E replace(E e){
        E ret = findMax();
        data.set(0,e);
        siftDown(0);
        return ret;
    }
  • heapify
    heapify(数组建堆)是传入一个数组,将数组转化的一个堆的方法,我们首先要在数组中定义一个构造方法,接收一个指定的数组将其储存到自身数组中
    public Array(E[] arr){
        data = (E[])new Object[arr.length];
        for(int i = 0 ; i

然后在堆中也添加一个入参为数组的构造器,首先调用Array的构造方法,为堆中的Array进行初始化,然后从堆中的最后一个不为叶子节点的节点开始,进行siftDown操作,直到根节点结束。这样一来就满足了堆的定义,那么如何找到最后一个不为叶子节点的节点呢?其实很容易,就是数组中最后一个元素的父亲节点索引所在的位置就是最后一个不为叶子节点的节点。即(size-2)/2所在的位置

    public MaxHeap(E[] arr){
        int i = parent(arr.length-1);
        data = new Array<>(arr);
        while(i>=0){
            siftDown(i);
            i--;
        }
    }

这样一来我们就将堆中的基本方法都写完了

2.优先队列(PriorityQueue)

一般的队列中,我们采用先进先出(First In First Out)的方式,但是这种排队方式有时并不适用,比如在医院中,病人等待治疗需要使用手术室,肯定是有一个优先级的,如果突然有优先级更高的患者(例如严重车祸)出现,那么他使用手术室的位置肯定是排在一些小手术患者的前面。

对于优先队列,首先,优先队列也满足队列的接口

public interface Queue {
    void enqueue(E e);
    E dequeue();
    E getFront();
    int getSize();
    boolean isEmpty();
}

由于底层我们使用一个最大堆实现,那么在入队的时候我们将元素放入堆中即可,出队就将元素从堆的根节点(优先级最大)的地方取出,getFront取出头元素就将堆中根节点元素返回,其他的基本操作都可以调用数组的操作实现。

public class PriorityQueue> implements Queue {

    private  MaxHeap maxHeap;

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

    @Override
    public int getSize(){
        return maxHeap.size();
    }

    @Override
    public boolean isEmpty(){
        return maxHeap.isEmpty();
    }

    @Override
    public E getFront(){
        return maxHeap.findMax();
    }

    @Override
    public void enqueue(E e){
        maxHeap.add(e);
    }

    @Override
    public E dequeue(){
        return maxHeap.extractMax();
    }


}

3.总结

  1. 二叉堆中最大堆和最小堆是一个相对的概念,优先级的高低都可以人为的通过Comparable接口中的compareTo方法进行定义。
  2. java的util包下的PriorityQueue中使用的是一个最小堆

你可能感兴趣的:(学习)