算法通关村第十四关白银挑战——堆的经典算法题

关注微信公众号:怒码少年。

回复关键词:【电子书】,领取多本计算机相关电子书

大家好,我是怒码少年小码。

今天开始进入新的篇章------堆!这里我默认了大家都知道堆的基本知识了,我们来看看关于堆的两道高频算法题吧。

数组中的第K个最大元素

LeetCode 215:给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

示例:

  • 输入: 3,2,1,5,6,4, k = 2
  • 输出: 5

分析:本题的方法有三种:选择法、快速排序和堆查找

  1. 选择法

类似于冒泡排序,第一个排序找出第1大的元素,第二次排序找出第2大的元素,第k次排序找出第k大的元素。

  1. 快速排序:之前已经讲过
  2. 堆查找

利用堆解决这个问题:首先思考用最大堆还是最小堆?答:最小堆。

原因:构造一个k大小的最小堆,则这个堆里存放的就是前k大的元素,只有比堆顶这个堆中最小的元素大,才能进入堆中。最后这个堆顶就是第k大的元素。

记忆口诀:

  • 查找:找大用小,大的进;找小用大,小的进
  • 排序:升序用小,降序用大。

[3,2,3,1,2,4,5,1,5,6,2,3]k=4。为例。注意:只有当前遍历的元素,大于堆顶元素才会入堆,否则丢弃。

堆的代码纯手写会很复杂,在Java中可以使用优先队列实现。

在 Java 中,PriorityQueue 是一个实现了优先队列(Priority Queue)的类。是基于优先级的队列,元素按照一定的优先级顺序进行排序并存储。

java 复制代码
 public int findKthLargest(int[] nums, int k) {
    //利用优先队列和最小堆完成
    if(k > nums.length){
        return -1;
    }
    int len = nums.length;
    //创建一个还有k个元素大小的最小堆
    PriorityQueue<Integer> minHeap = new PriorityQueue<>(k,(a,b)->a-b);
    for(int i =0 ; i<k ; i++){
        minHeap.add(nums[i]);
    }
    for(int i = k ; i <len ; i++){
        //获取,但是不拿出,因为还不确定是不是要换
        Integer topEle = minHeap.peek();
        //只要当前遍历的元素比堆顶元素大,堆顶弹出,当前遍历的放进去
        if(nums[i] > topEle){
          minHeap.poll();
          minHeap.offer(nums[i]);
        }
    }
    return minHeap.peek();
}

offer()方法的作用是将元素插入到优先队列中,并根据定义的优先级顺序进行排序,以便在后续的操作中能够按照优先级顺序提取元素。

合并 K 个升序链表

LeetCode 23:给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例:

  • 输入:lists = \[1,4,5,1,3,4,2,6]
  • 输出:1,1,2,3,4,4,5,6
  • 解释:链表数组如下: 1-\>4-\>5, 1-\>3-\>4, 2-\>6 将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6

分析:每个队列都是从小到大排序的,每次都要找最小的元素,我们要用小顶堆,不同的是每次比较谁更小

堆合并的策略:不管几个链表,最终都是按照顺序来的。每次都将剩余节点的最小值加到输出链表尾部,然后进行堆调整,最后堆空的时候,合并完成。

java 复制代码
public ListNode mergeKLists(ListNode[] lists) {
    if(lists == null || lists.length == 0) return null;
//创建优先队列
    PriorityQueue<ListNode> q = new PriorityQueue<>(Comparator.comparing(node -> node.val));

    for(int i =0;i<lists.length;i++){
        if(lists[i] != null){
            q.add(lists[i]);
        }
    }

    ListNode dummy = new ListNode(0);
    ListNode tail = dummy;

    while(!q.isEmpty()){
        tail.next = q.poll();
        tail = tail.next;
        if(tail.next != null){
            q.add(tail.next);
        }
    }
    return dummy.next;
}

PriorityQueue<ListNode> q = new PriorityQueue<>(Comparator.comparing(node -> node.val)) 这段代码的意思是创建了一个优先队列 q,并指定了节点的比较方式。

首先,使node -> node.val 是一个 Lambda 表达式,用于定义优先队列 q 的比较器。它表示一个匿名函数,接受 ListNode 类型的参数 node,并返回 node.val。

具体来说,node -> node.val 表达式的作用是根据链表节点的值 val 进行比较。当两个链表节点进行比较时,比较器会调用这个表达式来获取节点的值,并根据节点值的大小来决定它们的顺序。

通过 Comparator.comparing() 方法,我们可以将这个 Lambda 表达式作为比较器传递给 PriorityQueue 构造函数,以创建一个根据节点值排序的优先队列。也就是说,优先队列 q 中的节点会根据节点值的大小进行排序,使得队列中的节点始终以升序排列

通过循环遍历 lists 数组,对每个链表进行判断。如果当前链表不为空(即非空链表),就将该链表的头节点(即最小值节点)加入到优先队列 q 中。

进入循环,持续执行以下步骤,直到优先队列 q 为空:

  • 从优先队列 q 中取出当前最小的节点,并将其设为 tail 的下一个节点。
    将 tail 指向新的节点。
  • 如果 tail.next 不为空,说明当前的已合并链表还有剩余节点。
  • 返回新链表 dummy 的下一个节点,即合并后的有序链表的头节点。
相关推荐
江畔柳前堤3 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore4 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考6 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX7 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面7 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号8 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽8 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI8 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者10 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者10 小时前
【征程6】校准量化中HistogramObserver解析
算法