java堆和优先级队列PriorityQueue的使用及实现

在谈优先级队列之前先简单说一下堆的概念。

堆(heap)

一、概念
1.堆在逻辑上是一棵完全二叉树
2.物理上是保存在数组中
3.满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
4.反之,则是小堆,或者小根堆,或者最小堆
5.堆的基本作用是,快速找集合中的最值

二、大小根堆的建立向下调整实现
调整过程:
1.判断 index 位置有没有孩子
1)index 如果已经是叶子结点,则整个调整过程结束
2)因为堆是完全二叉树,没有左孩子就一定没有右孩子,所以判断是否有左孩子
3)因为堆的存储结构是数组,所以判断是否有左孩子即判断左孩子下标是否越界,即 left >= size 越界

2.确定 left 或 right,谁是 index 的最小孩子 min
1)如果右孩子不存在,则 min = left
2)否则,比较 array[left] 和 array[right] 值得大小,选择小的为 min

3.比较 array[index] 的值 和 array[min] 的值,如果 array[index] <= array[min],则满足堆的性质,调整结束

4.否则,交换 array[index] 和 array[min] 的值

5.然后因为 min 位置的堆的性质可能被破坏,所以把 min 视作 index,向下重复以上过程

//向下调整实现大根堆和小根堆
import java.util.Arrays;

public class ShiftDown {
    public static void swap(int[] arr, int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void shiftDownBig(int[] arr, int length, int parent){//向下调整大根堆
        int child = parent * 2 + 1;//左孩子下标
        while(child < length){
            if(child + 1< length && arr[child + 1] > arr[child]){//如果右孩子存在并且值大于左孩子,要比较的就是右孩子,更新child
                child++;
            }
            if(arr[child] > arr[parent]){//如果child位置的值大于parent位置的值
                swap(arr, child, parent);//交换两个值
                parent = child;//继续向下调整,更新parent和child的值
                child = parent * 2 + 1;
            }else{
                break;
            }
        }
    }

    public static void shiftDownSmall(int[] arr, int length, int parent){//向下调整小根堆
        int child = parent * 2 + 1;//左孩子下标
        while(child < length){
            if(child + 1 < length && arr[child + 1] < arr[child]){
                child++;
            }
            if(arr[child] < arr[parent]){
                swap(arr, child, parent);
                parent = child;
                child = parent * 2 + 1;
            }else{
                break;
            }
        }
    }

    public static void creatBigHeap(int[] arr){//建立大根堆
        for(int i = (arr.length - 2) / 2; i >= 0; i--){//从最后一个非叶子节点开始向下调整建立大根堆
            shiftDownBig(arr, arr.length, i);
        }
    }

    public static void creatSmallHeap(int[] arr){//建立小根堆
        for(int i = (arr.length - 2) / 2; i >= 0; i--){//从最后一个非叶子节点开始向下调整建立小根堆
            shiftDownSmall(arr, arr.length, i);
        }
    }


    public static void main(String[] args) {
        int[] arr = {1, 4, 5, 3, 19, 22, 2};
        int[] arr1 = arr.clone();
        creatBigHeap(arr);
        System.out.println(Arrays.toString(arr));
        creatSmallHeap(arr1);
        System.out.println(Arrays.toString(arr1));
    }
}

优先级队列PriorityQueue

优先级队列是一种特别的队列,是一种按照对象优先级进行数据处理的一种数据结构,每次处理都会最先处理优先级最高的对象。有两个最基本的操作,一个是返回最高优先级对象(出队列),一个是添加新的对象(入队列)。优先级队列不允许为null。

一、优先级队列的使用
Java中使用优先级队列PriorityQueue对对象进行优先级排序需要实现Comparable或Comparator接口用来给对象排序,不实现接口会默认按照自然顺序排序(小根堆)。

1.创建小根堆:

import java.util.Comparator;
import java.util.PriorityQueue;

class compareQueue implements Comparator{
    @Override
    public int compare(Integer a, Integer b){
        return a - b;
    }
}
public class MyPriorityQueue {
    public static void main(String[] args) {
        PriorityQueue queue = new PriorityQueue<>(new compareQueue());
        queue.offer(5);
        queue.offer(6);
        queue.offer(4);
        queue.offer(3);
        queue.offer(7);
        queue.offer(2);
        System.out.println(queue);
    }
}

2.创建大根堆:

import java.util.Comparator;
import java.util.PriorityQueue;

public class MyPriorityQueue {
    public static void main(String[] args) {
        PriorityQueue queue = new PriorityQueue<>(new Comparator(){
            @Override
            public int compare(Integer a, Integer b) {
                return b - a;
            }
        });
        queue.offer(5);
        queue.offer(6);
        queue.offer(4);
        queue.offer(3);
        queue.offer(7);
        queue.offer(2);
        System.out.println(queue);
    }
}

二、优先级队列的实现
一般用堆来实现优先级队列
1.入队列
以大根堆为例:
1.首先按尾插方式放入数组
2.比较其和其父亲的值的大小,如果父亲的值大,则满足堆的性质,插入结束
3.否则,交换其和父亲位置的值,重新进行 2、3 步骤
4.直到根结点

2.出队列
出队列出来的元素是优先级最高的。其中为了防止破坏堆的结构,删除时并不是直接将堆顶元素删除,而是用数组的最后一个元素替换堆顶元素,然后通过向下调整方式重新调整成堆。

代码实现

//优先级队列入队、出队、获取队首元素、判空操作的实现
import java.util.Arrays;
import java.util.PriorityQueue;

public class MyPriorityQueue {
    private int[] arr = new int[10];
    private int size = 0;

    public void shiftUpBig(int[] arr, int child){//大根堆向上调整
        int parent = (child - 1) / 2;
        while(child > 0) {
            if (arr[child] > arr[parent]) {
                ShiftDown.swap(arr, child, parent);
                child = parent;
                parent = (child - 1) / 2;
            } else {
                break;
            }
        }
    }

    public void shiftUpSmall(int[] arr, int child){//小根堆向上调整
        int parent = (child - 1) / 2;
        while(child > 0) {
            if (arr[child] < arr[parent]) {
                ShiftDown.swap(arr, child, parent);
                child = parent;
                parent = (child - 1) / 2;
            } else {
                break;
            }
        }
    }

    public void offer(int value){//入队
        if(size == arr.length){//扩容
            Arrays.copyOf(arr, arr.length * 2);
        }
        arr[size++] = value;//尾插
        shiftUpSmall(arr, size - 1);//向上调整
    }

    public int poll(){//出队
        if(size > 0){//如果队中有元素
            int poll = arr[0];//记录队首元素
            ShiftDown.swap(arr, 0, size - 1);//交换队首与队尾元素
            size--;//删除队尾元素
            ShiftDown.shiftDownSmall(arr, size, 0);//将队首元素调整下去
            return poll;//返回记录的队首元素
        }
        return -1;
    }

    public int peek(){//获取队首元素
        if(size > 0){
            return arr[0];//返回队首元素
        }
        return -1;
    }

    public boolean isEmpty(){//判断队列是否为空
        return size == 0;
    }

    public static void main(String[] args) {
        PriorityQueue pq = new PriorityQueue<>();
        pq.offer(9);
        pq.offer(2);
        pq.offer(10);
        pq.offer(15);
        pq.offer(3);
        pq.offer(1);
        pq.offer(7);
        pq.offer(23);
        pq.offer(89);
        while(!pq.isEmpty()){
            System.out.print(pq.poll() + " ");
        }
        System.out.println();
        System.out.println("My PriorityQueue");
        MyPriorityQueue mpq = new MyPriorityQueue();
        mpq.offer(9);
        mpq.offer(2);
        mpq.offer(10);
        mpq.offer(15);
        mpq.offer(3);
        mpq.offer(1);
        mpq.offer(7);
        mpq.offer(23);
        mpq.offer(89);
        while(!mpq.isEmpty()){
            System.out.print(mpq.poll() + " ");
        }
        System.out.println();
    }
}

你可能感兴趣的:(java,算法与数据结构)