优先级队列(堆)的综合练习

优先级队列(堆)的综合练习

什么是优先级队列(堆)

  优先级队列通常使用堆来构建,在逻辑上是一颗二叉树,但在物理上堆是储存在数组中的,堆的基本作用是快速找到集合中的最值。堆分为大堆和小堆,所有节点的值都大于其子树节点的值的堆叫做大堆(大根堆),所有节点的值都小于其子树节点的值叫做小堆(小根堆)。

堆的基本操作

向下调整
  从上到下依次调整每棵树,将每棵树都调整最根节点的值大于左右子树节点,从而将整个堆调整为大堆;或调整为根节点的值小于左右子树节点的值,将整个堆调整为小堆,这个过程叫做向下调整。

    public void shiftDown(int[] arr,int size,int index){//调整为最小堆
        int left = 2 * index + 1;
        while (left < size){
            int min = left;
            int right = 2 * index + 2;
            if (right < size){
                if (arr[right] < arr[left])min = right;
            }
            if (arr[index] < arr[min])break;
            int t = arr[index];
            arr[index] = arr[min];
            arr[min] = t;
            index = min;
            left = 2 * index +1;
        }
    }

入队列
  首先按照尾插的方式把数据放入队尾,然后进行向上调整

    public void offer(int[]arr,int index){//入队列,向上调整
        while (index > 0){
            int parent = (index - 1) / 2;
            if (arr[parent] >= arr[index])break;
            int tmp = arr[parent];
            arr[parent] = arr[index];
            arr[index] = tmp;
            index = parent;
        }
    }

出队列
  出队列时是吧堆顶的元素返回出去,但是并不是直接删除,是用数组中最后一个元素替换堆顶元素,然后在进行一次向下调整即可。

    public int poll(int[] arr){
        int tmp = arr[0];
        arr[0] = arr[--size];
        shiftDown(arr,size,0);
        return tmp;
    }

堆相关题目

查找和最小的K对数字

    /*
    给定两个以升序排列的整形数组 nums1 和 nums2, 以及一个整数 k。
    定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2。
    找到和最小的 k 对数字 (u1,v1), (u2,v2) ... (uk,vk)。
    链接:https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums
     */
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        PriorityQueue<List<Integer>> priorityQueue = new PriorityQueue<>(k, new Comparator<List<Integer>>() {
            @Override
            public int compare(List<Integer> o1, List<Integer> o2) {
                return (o2.get(0) + o2.get(1)) - (o1.get(0) + o1.get(1));
            }
        });
        for (int i = 0; i < Math.min(nums1.length, k); i++) {
            for (int j = 0; j < Math.min(nums2.length, k); j++) {
                if (priorityQueue.size() == k && nums1[i] + nums2[j] > priorityQueue.peek().get(0) + priorityQueue.peek().get(1))
                    break;
                if (priorityQueue.size() == k) priorityQueue.poll();
                List<Integer> list = new ArrayList<>();
                list.add(nums1[i]);
                list.add(nums2[j]);
                priorityQueue.add(list);
            }
        }
        List<List<Integer>> ret = new ArrayList<>();
        while (!priorityQueue.isEmpty()) ret.add(priorityQueue.poll());
        return ret;
    }

最后一块石头的重量

    /*
    有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。
     */

    public int lastStoneWeight(int[] stones) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        for (int n : stones) priorityQueue.offer(n);
        int x = 0;
        int y = 0;
        while (!priorityQueue.isEmpty()) {
            x = priorityQueue.poll();
            if (!priorityQueue.isEmpty()) y = priorityQueue.poll();
            else {
                y = -1;
                break;
            }
            if (x != y) priorityQueue.offer(Math.abs(x - y));
        }
        if (y == -1) return x;
        return 0;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值