算法通关村——解析堆在数组和链表的应用

1. 堆

1.1 什么是堆?

堆是将一组数据以完全二叉树的形式存储在数组里面。一般有大根堆和小根堆。
小根堆:任意节点的值小于等于它的左右孩子,最小值在堆顶。
大根堆:任意节点的值大于等于它的左右还是,最大值在堆顶。
java里面采用PriorityQueue,然后可以自定义构建小根堆,大根堆。
算法通关村——解析堆在数组和链表的应用_第1张图片

2. 数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

2.1 小根堆

主要思路就是使用一个小根堆来存储前k个元素,然后再遍历k后面的元素,如果有元素大于队首元素,队首元素出队,该元素入队,否则继续遍历。

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

  1. k=2,创建容量为2的小根堆,先添加3,2元素,堆元素[2,3]
  2. 1进入,1<2, 继续
  3. 5入队,5>2,2出队,5入队,堆元素[3,5]
  4. 6入队,6>3,3出队,6入队,堆元素[5,6]
  5. 4<5,继续,退出,第k个最大元素就是5
 public int findKthLargest(int[] nums, int k) {
    int len = nums.length;

     if(k>len){
         return -1;
     }

     // 小根堆
     PriorityQueue<Integer> pq = new PriorityQueue<Integer>(k,(a,b)->a-b);
     // 添加前k个元素
     for(int i=0;i<k;i++){
         pq.add(nums[i]);
     }

     for(int i=k;i<len;i++){
         // 堆顶元素
         Integer top = pq.peek();
         // 堆顶元素小,堆顶元素出栈,当前元素入栈
         if(nums[i] > top){
             pq.poll();
             pq.offer(nums[i]);
         }
     }

     return pq.peek();
    }

3. 合并 K 个升序链表

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

3.1 小根堆

构建一个小根堆,让里面的每个列表的头节点添加到堆里面,然后创建一个新的链表,将最小的元素添加到链表里面,这个最小的元素后面如果还有其他元素的话,就将其他元素添加至堆里面,进一步排序,然后再刷选出最小的节点。

输入: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

堆里面初始:头节点 1,1,2
第一次合并到新链表,1里面还剩[1,3,4],[2,6],[4,5] 头节点1,2,4 , 链表 1 ->
第二次合并 [2,6][3,4] [4,5], 头节点2,3,4, 链表 1->1->
第三次合并 [6][3,4][4,5] 头节点3,4,5,链表 1->1->2
依次合并

public ListNode mergeKLists(ListNode[] lists) {
      if(lists == null){
        return null;
      }
      // 构建小根堆
      PriorityQueue<ListNode> pq = new PriorityQueue<ListNode>(Comparator.comparing(node->node.val));
      for(int i=0;i<lists.length;i++){
        if(lists[i]!=null){
          pq.add(lists[i]);
        }
      }

      ListNode sentinel = new ListNode(-1);
      ListNode res = sentinel;

      while(!pq.isEmpty()){
        // 合并后链表里面最小值
        res.next = pq.poll();
        res = res.next;
        if(res.next!=null){
          pq.add(res.next);
        }
      }

      return sentinel.next;
    }

真要写起来的话还是先写两个链表合并,然后再升级多个链表合并,对于堆里面的有些性质还不是太熟悉的,所以一时还是想不到这种方法。

你可能感兴趣的:(算法,算法)