【JAVA数据结构】堆高频面试题总结

总有一场相遇是互相喜欢的

在这里插入图片描述

大家好,这里是新一,请多关照🙈🙉🙊。在本篇博客中,新一将会为大家介绍数据结构与算法之堆面试高频题,堆在面试中也时不时会考到,在这里新一为大家整理堆面试高频题,为了方便大家理解,新一特地给大家附上了 源码和图片 便于大家理解,干货满满哟。(以下结果均在IDEA中编译)希望在方便自己复习的同时也能帮助到大家。😜😜😜🎪🚀🧰

以下是我们的文章



一.🎪 Top-K问题

在面试中面试官可能会问我们以下问题:给你100w个数据,设计一个算法找到你前10个最大的元素

1.1 🚀 各种思路

这里我们给出以下几种解决方法:

思路一:从小到大排序,再输出后10个元素,这虽然是一种解决方法,但面试官一般不会让你这样做,那样这个问题就没有意义了

思路二:将这一百万个元素整体建成一个大根堆,再依次出队10个元素即可,但占用的内存是不是有点多呢?

思路三:只建k个元素的小根堆,如果后续元素比堆顶元素大,那么先出队,然后再将这个元素入队,当遍历完整个数据时,最后小根堆上即为我们的top-k,显然是我们的思路三比较好

此外,还延伸出了以下类似于top-k的问题

求前k个最大元素,建一个小根堆
求前k个最小元素, 建一个大根堆
求第k大的元素,建一个小堆,堆顶元素就是第k大的元素
求第k小的元素,建一个大堆,堆顶元素就是第k小的元素

1.2 🚀 JAVA中对象的比较

既然牵扯到最值问题,那么就肯定少不了对象的比较,如果我们要比较的数据类型是引用类型呢?比如说我们有如下对象:

class Card {
    public int rank; // 数值
    public String suit; // 花色

    public Card(int rank, String suit) {
        this.rank = rank; this.suit = suit;
    }



    //自定义类型一旦牵扯到比较,一定要重写comparable或者comparator接口

    @Override
    public String toString() {
        return "Card{" +
                "rank=" + rank +
                ", suit='" + suit + '\'' +
                '}';
    }
}

我们要根据扑克牌的数值进行比较,可以吗?

public static void main(String[] args) {
        Card card1 = new Card(2,"♥");
        Card card2 = new Card(1,"♥");

        PriorityQueue<Card> priorityQueue = new PriorityQueue<>();
        priorityQueue.offer(card1);
        priorityQueue.offer(card2);
    }

在这里插入图片描述
所以这里必须借助comparable或者comparator接口,下面我们用内部类来实现引用类型的比较:

public static void main(String[] args) {
        Card card1 = new Card(2,"♥");
        Card card2 = new Card(1,"♥");
        //RankComparator rankComparator = new RankComparator();

        //内部类 - 推荐
        PriorityQueue<Card> priorityQueue = new PriorityQueue<>(new Comparator<Card>() {
            @Override
            public int compare(Card o1, Card o2) {
                return o1.rank - o2.rank;
            }
        });

        //lambda表达式 - 可读性差
        //PriorityQueue<Card> priorityQueue = new PriorityQueue<>((x,y)->{return x.rank - y.rank;});
        priorityQueue.offer(card1);
        priorityQueue.offer(card2);
        System.out.println(priorityQueue);
    }

在这里插入图片描述

1.3 🚀 top-k代码实现

public class TopK {

    /**
     * 求数组中前K个最小的元素
     * @param array
     * @param k
     * @return
     */
    public static int[] topk(int[] array ,int k){
        //1.创建一个大小为k的大根堆
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        //2.遍历数组中当中的元素,前k个元素放到队列中
        for (int i = 0; i < array.length; i++) {
            if (maxHeap.size() < k){
                maxHeap.offer(array[i]);
            }else {
                int top = maxHeap.peek();
                if (top > array[i]){
                    maxHeap.poll();
                    maxHeap.offer(array[i]);//将新元素放入其中
                }
            }
        }
        int[] ret = new int[k];
        for (int i = 0; i < k; i++) {
            ret[i] = maxHeap.poll();
        }
        return ret;
    }
    public static void main(String[] args) {
        int[] array = {18,21,8,10,34,21};
        int[] ret = topk(array, 3);
        System.out.println(Arrays.toString(ret));
    }
}

在这里插入图片描述
下面我们来一道紧张刺激的力扣:373.查找和最小的k对数字

在这里插入图片描述

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        List<List<Integer>> ans = new ArrayList<>();
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> (nums1[a[0]] + nums2[a[1]]) - (nums1[b[0]] + nums2[b[1]]));
        for (int i = 0; i < Math.min(nums1.length, k); i++) {
            pq.add(new int[]{i, 0});
        }
        while (k > 0 && !pq.isEmpty()) {
            int[] idx = pq.poll();
            ans.add(List.of(nums1[idx[0]], nums2[idx[1]]));
            if (idx[1] + 1 < nums2.length) {
                pq.add(new int[]{idx[0], idx[1] + 1});
            }
            k--;
        }
        return ans;
    }
}

在这里插入图片描述

二.🎪 堆排序

我们要对一个堆进行排序,即使堆底层的数组变得有序,我们要使大根堆变得有序,那么每次只需将堆顶元素跟最后一个元素交换即可,交换后再向下调整即可实现堆排序;

public class TestHeap {
    public int[] elem;
    int usedSize;

    public TestHeap() {
        this.elem = new int[10];
    }

    /**
     * 向下调整
     * @param parent 每棵树的根节点
     * @param len 每棵树调整的结束位置
     */
    public void shiftDown(int parent, int len) {
        int child = 2 * parent + 1;

        //数学公式推导 - 错位相减 - 时间复杂度O(n)
        //1.循环依次修改位置
        while (child < len) {//向下调整一直到根
            if (child + 1 < len && elem[child] < elem[child + 1]){//判断是否已经是叶子结点或者当前堆已经为大根堆
                child++;
            }
            if (elem[parent] < elem[child]){//交换
                int ret = elem[parent];
                elem[parent] = elem[child];
                elem[child] = ret;
            }else{
                break;
            }
            parent = child;//向下调整具体操作
            child = child * 2 + 1;
        }
    }
    public void createHeap(int[] array){
        for (int i = 0; i < array.length; i++) {
            elem[i] = array[i];
            usedSize++;
        }
        for (int parent = (usedSize - 2) / 2; parent >= 0; parent--) {
            shiftDown(parent,usedSize);
        }
    }

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

    public void offer(int val){
        if (isFull()){
            elem = Arrays.copyOf(elem, 2 * elem.length);
        }
        elem[usedSize++] = val;
        shiftUp(usedSize - 1);
    }

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

    public int poll(){
        if (isEmpty()){
            throw new RuntimeException("优先级队列为空!");
        }
        int ret = elem[0];
        elem[0] = elem[usedSize - 1];
        elem[usedSize - 1] = ret;
        usedSize--;
        shiftDown(0,usedSize);

        return ret;
    }

    public boolean isEmpty() {
        return usedSize == 0;
    }

    public void heapSort(){
        for (int end = this.usedSize - 1 ; end > 0; end--) {
            int ret = elem[0];
            elem[0] = elem[end];
            elem[end] = ret;
            shiftDown(0, end);
        }
    }
}

想对大家说的话

💕家人们,学到这里我们的数据结构与算法中的堆面试高频题已经彻底弄懂啦,如果觉得新一讲得还清楚的话,可以点个赞支持一下哦🥳🥳🥳后续新一会持续更新JAVA的有关内容,学习永无止境,技术宅,拯救世界!
在这里插入图片描述

  • 16
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 27
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Corwttaml

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

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

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

打赏作者

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

抵扣说明:

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

余额充值