Java优先级队列(堆)

目录

1.堆的性质

2.堆的存储方式

3.堆的创建

4.堆的增删查改

4.1 offer() 增添元素

4.2 peek() 获取堆顶元素

4.3 pop() 弹出堆顶第一个元素并返回

5.堆排序


1.堆的性质

大根堆:根节点为最大的堆。

小根堆:根节点为最小的堆。

堆总是一颗完全二叉树。

2.堆的存储方式

堆的底层是一个数组,但是在应用的时候会将其看成一颗完全二叉树,所以他也有一些二叉树的性质:

假设i为元素的下标

(1)i不为0的节点的双亲节点的下标为:(i-1)/ 2;如果i为0,那i就是根节点。

(2)下标为i的节点的左孩子下标:i*2+1,否则没有左孩子

(3)下标为i的节点的右孩子下标:i*2+2,否则没有右孩子

3.堆的创建

堆的底层是一个数组,所以在创建堆之前要先建立一个数组

public class TestHeap {
    public int[] elem;
    public int usedSize;//数组元素个数
    public static final int DEFAULT_SIZE = 10;//数组的初始容量

    public TestHeap() {
        elem = new int[DEFAULT_SIZE];
    }
}

我们以大根堆为例

既然是创建大根堆,那么它的每颗子树都需要保证根节点是该树的最大值,所以我们从最后一个有孩子的子树开始对堆依此进行向下调整:

首先找到该子树的左孩子节点,然后进入循环,每次找到左右孩子的最大值,让其与双亲节点相比较,大的放到双亲节点,然后让双亲节点指向孩子节点,进行循环操作,直到双亲节点的下标超过节点总数(等于也不行)

    public void createHeap(int[] array) {
        for (int i = 0; i < array.length; i++) {
            elem[i] = array[i];
            usedSize++;
        }
        for (int parent = (usedSize - 1 - 1) / 2; parent >= 0; parent--) {
            shiftDown(parent, usedSize);//向下调整
        }
    }

    public void shiftDown(int parent, int len) {
        int child = parent * 2 + 1;
        while (child < len) {
            if (child + 1 < len && elem[child] < elem[child + 1]) {
                child++;
            }
            if (elem[parent] < elem[child]) {
                int tmp = elem[parent];
                elem[parent] = elem[child];
                elem[child] = tmp;
                parent = child;
                child = parent * 2 + 1;
            } else {
                break;
            }
        }
    }

4.堆的增删查改

4.1 offer() 增添元素

将新增的元素放到队尾,然后对其进行向上调整,和向下调整类似,只不过顺序由下向上,并且新增的元素一次只有一个,所以只需要进行一次即可

    public void offer(int val) {
        if (isFull()) {
            //扩容
            this.elem = Arrays.copyOf(this.elem, this.elem.length * 2);
        }
        this.elem[this.usedSize] = val;
        this.usedSize++;
        //向上调整
        shiftUp(usedSize - 1);
    }

    private boolean isFull() {
        return this.usedSize == this.elem.length;
    }

    public void shiftUp(int child) {
        int parent = (child - 1) / 2;
        while (parent >= 0) {
            if (elem[parent] < elem[child]) {
                int tmp = elem[parent];
                elem[parent] = elem[child];
                elem[child] = tmp;
                child = parent;
                parent = (parent - 1) / 2;
            } else {
                break;
            }
        }
    }

4.2 peek() 获取堆顶元素

直接返回数组的第一个元素

    //判断堆是否为空
    private boolean isEmpty() {
        return this.usedSize == 0;
    }

    public int peek() {
        if (isEmpty()) {
            return -1;
        }
        return this.elem[0];
    }

4.3 pop() 弹出堆顶第一个元素并返回

弹出元素相当于删除元素,将首元素和尾元素交换将其删除,然后进行向下调整

    public int pop() {
        if (isEmpty()) {
            return -1;
        }
        int tmp = elem[0];
        elem[0] = elem[this.usedSize - 1];
        elem[this.usedSize - 1] = tmp;
        //删除最后一个元素
        this.usedSize--;
        //向下调整
        shiftDown(0, usedSize);
        return tmp;
    }

5.堆排序

对堆进行升序排序,需要建立大根堆,每次将堆顶元素放到相对应的尾部,然后再进行像下调整,比如:第一次放到到i位置,下一次放到i-1位置,以此类推……

    public void heapSort() {
        int end = usedSize - 1;
        while (end > 0) {
            //将堆顶元素与其相对的位置交换
            int tmp = elem[end];
            elem[end] = elem[0];
            elem[0] = tmp;
            //向下调整,将最大值放到堆顶
            shiftDown(0, end);
            end--;
        }
    }

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

追梦不止~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值