LeetCode 刷题汇总之堆

前言

本文隶属于专栏《LeetCode 刷题汇总》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!

本专栏目录结构请见LeetCode 刷题汇总

正文

23. 合并K个升序链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) return null;
        
        // 优先队列
        PriorityQueue<ListNode> queue= new PriorityQueue<>(lists.length, (o1, o2) -> {
            if (o1.val < o2.val) return -1;
            else if (o1.val == o2.val) return 0;
            else return 1;
        });
        
        // 单链表中常见的哨兵
        ListNode dummy = new ListNode(0), cur = dummy;
        
        for (ListNode node : lists)
            if (node != null) queue.add(node);
                
        while (!queue.isEmpty()){
            cur.next = queue.poll();
            cur = cur.next;
            if (cur.next != null) queue.add(cur.next);
        }
        return dummy.next;
    }
}
/**
 * Definition for singly-linked list.
 * class ListNode(_x: Int = 0, _next: ListNode = null) {
 *   var next: ListNode = _next
 *   var x: Int = _x
 * }
 */
object Solution {
  def mergeKLists(lists: Array[ListNode]): ListNode = {
    if(lists.isEmpty) return null
    var len = lists.length
    while(len != 1){
      // 两两合并
      for(i <- 0 until len / 2) lists(i) = mergeTwo(lists(i * 2), lists(i * 2 + 1))
      // 链表数组长度为奇数,会多出一个链表
      if(len % 2 == 1) lists(len / 2) = lists(len - 1)
      // 每次长度减半
      len = (len + 1) / 2
    }
    lists(0)
  }

  /**
   * 合并两个单链表
   * @param node1 链表 1
   * @param node2 链表 2
   * @return 返回合并后的链表
   */
  private def mergeTwo(node1: ListNode, node2: ListNode): ListNode = {
    if(node1 == null) return node2;
    if(node2 == null) return node1;
    val dummy = new ListNode(0)
    var cur = dummy
    var cur1 = node1
    var cur2 = node2
    while(cur1 != null || cur2 != null){
      if(cur2 == null || cur1 != null && cur1.x < cur2.x){
        cur.next = cur1
        cur1 = cur1.next
      }else{
        cur.next = cur2
        cur2 = cur2.next
      }
      cur = cur.next
    }
    dummy.next
  }
}

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

堆排序选择

class Solution {
    public int findKthLargest(int[] nums, int k) {
        int heapSize = nums.length;
        // 建堆
        buildMaxHeap(nums, heapSize);
        for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
            // 删除
            swap(nums, 0, i);
            --heapSize;
            // 调整
            maxHeapify(nums, 0, heapSize);
        }
        return nums[0];
    }

    public void buildMaxHeap(int[] a, int heapSize) {
        for (int i = heapSize / 2; i >= 0; --i) {
            maxHeapify(a, i, heapSize);
        } 
    }

    public void maxHeapify(int[] a, int i, int heapSize) {
        int l = i * 2 + 1, r = i * 2 + 2, largest = i;
        if (l < heapSize && a[l] > a[largest]) {
            largest = l;
        } 
        if (r < heapSize && a[r] > a[largest]) {
            largest = r;
        }
        if (largest != i) {
            swap(a, i, largest);
            maxHeapify(a, largest, heapSize);
        }
    }

    public void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

优先队列

import scala.collection.mutable.PriorityQueue

object Solution {
  def findKthLargest(nums: Array[Int], k: Int): Int = {
    val priorityQueue = PriorityQueue[Int]()
    nums.foreach(priorityQueue.enqueue(_))
    for (_ <- 0 until k - 1) priorityQueue.dequeue()
    priorityQueue.dequeue()
  }
}

快速选择算法

import scala.util.Random
object Solution {

  def findKthLargest(nums: Array[Int], k: Int): Int = {
    val len = nums.length
    val targetIndex = len - k
    var low = 0
    var high = len - 1
    while (low <= high) {
      val i = partition(nums, low, high)
      if (i == targetIndex) return nums(i)
      else if (i < targetIndex) low = i + 1
      else high = i - 1
    }
    -1
  }

  /**
    * 分区函数,将 arr[high] 作为 pivot 分区点
    * i、j 两个指针,i 作为标记“已处理区间”和“未处理区间”的分界点,也即 i 左边的(low~i-1)都是“已处理区”。
    * j 指针遍历数组,当 arr[j] 小于 pivot 时,就把 arr[j] 放到“已处理区间”的尾部,也即是 arr[i] 所在位置
    * 因此 swap(arr, i, j) 然后 i 指针后移,i++
    * 直到 j 遍历到数组末尾 arr[high],将 arr[i] 和 arr[high](pivot点) 进行交换,返回下标 i,就是分区点的下标。
    */
  private def partition(arr: Array[Int], low: Int, high: Int): Int = {
    if (high > low) {
      //在下标 low 和 high 之间随机选择,然后和下标 high 元素进行交换
      val random = low + Random.nextInt(high - low)
      swap(arr, high, random)
    }
    var i = low
    val pivot = arr(high)
    for (j <- low until high if arr(j) < pivot) {
      swap(arr, i, j)
      i += 1
    }
    swap(arr, i, high)
    i
  }

  private def swap(arr: Array[Int], i: Int, j: Int): Unit = {
    val tmp = arr(i)
    arr(i) = arr(j)
    arr(j) = tmp
  }
}

239. 滑动窗口最大值

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        PriorityQueue<int[]> pq = new PriorityQueue<>((p1, p2) -> p1[0] != p2[0] ? p2[0] - p1[0] : p2[1] - p1[1]);
        for (int i = 0; i < k; ++i) {
            pq.offer(new int[]{nums[i], i});
        }
        int[] ans = new int[n - k + 1];
        ans[0] = pq.peek()[0];
        for (int i = k; i < n; ++i) {
            pq.offer(new int[]{nums[i], i});
            while (pq.peek()[1] <= i - k) {
                pq.poll();
            }
            ans[i - k + 1] = pq.peek()[0];
        }
        return ans;
    }
}

347. 前 K 个高频元素

public class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Arrays.sort(nums);
        int index = 0, cur, pre = nums[0];
        PriorityQueue<Unit> queue = new PriorityQueue<>(Comparator.comparingInt(Unit::getCount).reversed());
        for (int i = 1; i < nums.length; i++) {
            cur = nums[i];
            if (cur != pre) {
                queue.add(new Unit(pre, i - index));
                pre = cur;
                index = i;
            }
        }
        queue.add(new Unit(pre, nums.length - index));
        index = 0;
        int[] res = new int[k];
        while (index < k) {
            res[index] = queue.poll().getValue();
            index++;
        }
        return res;
    }

    private static class Unit {
        private final int value;
        private final int count;

        Unit(int value, int count) {
            this.value = value;
            this.count = count;
        }

        public int getValue() {
            return value;
        }

        public int getCount() {
            return count;
        }
    }
}

355. 设计推特

public class Twitter {

    /**
     * 用户 id 和推文(单链表)的对应关系
     */
    private Map<Integer, Tweet> twitter;

    /**
     * 用户 id 和他关注的用户列表的对应关系
     */
    private Map<Integer, Set<Integer>> followings;

    /**
     * 全局使用的时间戳字段,用户每发布一条推文之前 + 1
     */
    private static int timestamp = 0;

    /**
     * 合并 k 组推文使用的数据结构(可以在方法里创建使用),声明成全局变量非必需,视个人情况使用
     */
    private static PriorityQueue<Tweet> maxHeap;

    /**
     * Initialize your data structure here.
     */
    public Twitter() {
        followings = new HashMap<>();
        twitter = new HashMap<>();
        maxHeap = new PriorityQueue<>((o1, o2) -> -o1.timestamp + o2.timestamp);
    }

    /**
     * Compose a new tweet.
     */
    public void postTweet(int userId, int tweetId) {
        timestamp++;
        if (twitter.containsKey(userId)) {
            Tweet oldHead = twitter.get(userId);
            Tweet newHead = new Tweet(tweetId, timestamp);
            newHead.next = oldHead;
            twitter.put(userId, newHead);
        } else {
            twitter.put(userId, new Tweet(tweetId, timestamp));
        }
    }

    /**
     * Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent.
     */
    public List<Integer> getNewsFeed(int userId) {
        // 由于是全局使用的,使用之前需要清空
        maxHeap.clear();

        // 如果自己发了推文也要算上
        if (twitter.containsKey(userId)) {
            maxHeap.offer(twitter.get(userId));
        }

        Set<Integer> followingList = followings.get(userId);
        if (followingList != null && followingList.size() > 0) {
            for (Integer followingId : followingList) {
                Tweet tweet = twitter.get(followingId);
                if (tweet != null) {
                    maxHeap.offer(tweet);
                }
            }
        }

        List<Integer> res = new ArrayList<>(10);
        int count = 0;
        while (!maxHeap.isEmpty() && count < 10) {
            Tweet head = maxHeap.poll();
            res.add(head.id);

            // 这里最好的操作应该是 replace,但是 Java 没有提供
            if (head.next != null) {
                maxHeap.offer(head.next);
            }
            count++;
        }
        return res;
    }


    /**
     * Follower follows a followee. If the operation is invalid, it should be a no-op.
     *
     * @param followerId 发起关注者 id
     * @param followeeId 被关注者 id
     */
    public void follow(int followerId, int followeeId) {
        // 被关注人不能是自己
        if (followeeId == followerId) {
            return;
        }

        // 获取我自己的关注列表
        Set<Integer> followingList = followings.get(followerId);
        if (followingList == null) {
            Set<Integer> init = new HashSet<>();
            init.add(followeeId);
            followings.put(followerId, init);
        } else {
            if (followingList.contains(followeeId)) {
                return;
            }
            followingList.add(followeeId);
        }
    }


    /**
     * Follower unfollows a followee. If the operation is invalid, it should be a no-op.
     *
     * @param followerId 发起取消关注的人的 id
     * @param followeeId 被取消关注的人的 id
     */
    public void unfollow(int followerId, int followeeId) {
        if (followeeId == followerId) {
            return;
        }

        // 获取我自己的关注列表
        Set<Integer> followingList = followings.get(followerId);

        if (followingList == null) {
            return;
        }
        // 这里删除之前无需做判断,因为查找是否存在以后,就可以删除,反正删除之前都要查找
        followingList.remove(followeeId);
    }

    /**
     * 推文类,是一个单链表(结点视角)
     */
    private class Tweet {
        /**
         * 推文 id
         */
        private int id;

        /**
         * 发推文的时间戳
         */
        private int timestamp;
        private Tweet next;

        public Tweet(int id, int timestamp) {
            this.id = id;
            this.timestamp = timestamp;
        }
    }

    public static void main(String[] args) {

        Twitter twitter = new Twitter();
        twitter.postTweet(1, 1);
        List<Integer> res1 = twitter.getNewsFeed(1);
        System.out.println(res1);

        twitter.follow(2, 1);

        List<Integer> res2 = twitter.getNewsFeed(2);
        System.out.println(res2);

        twitter.unfollow(2, 1);

        List<Integer> res3 = twitter.getNewsFeed(2);
        System.out.println(res3);
    }
}
class Twitter {

    private Map<Integer, List<Integer>> userMap;
    private Map<Integer, Integer> twitterMap;
    private Map<Integer, List<Integer>> followMap;
    private Set<Integer> set;
    private int timestamp;

    /**
     * Initialize your data structure here.
     */
    public Twitter() {
        userMap = new HashMap<>();
        twitterMap = new HashMap<>();
        followMap = new HashMap<>();
        set = new TreeSet<>((x1, x2) -> -Integer.compare(twitterMap.get(x1), twitterMap.get(x2)));
        timestamp = 0;
    }

    /**
     * Compose a new tweet.
     */
    public void postTweet(int userId, int tweetId) {
        if (userMap.containsKey(userId)) {
            userMap.get(userId).add(tweetId);
        } else {
            List<Integer> twitterIds = new LinkedList<>();
            twitterIds.add(tweetId);
            userMap.put(userId, twitterIds);
        }
        twitterMap.put(tweetId, timestamp++);
        if (!followMap.containsKey(userId)) {
            List<Integer> followees = new LinkedList<>();
            followees.add(userId);
            followMap.put(userId, followees);
        }
    }

    /**
     * Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent.
     */
    public List<Integer> getNewsFeed(int userId) {
        List<Integer> followees = followMap.get(userId);
        if (followees == null || followees.isEmpty()) {
            return new ArrayList<>(0);
        }
        set.clear();
        for (int followee : followees) {
            if (userMap.containsKey(followee)) {
                List<Integer> list = userMap.get(followee);
                if (list == null || list.isEmpty()) {
                    continue;
                }
                set.addAll(list);
            }
        }
        List<Integer> res = new ArrayList<>(set);
        if (res.size() <= 10) {
            return res;
        }
        return res.subList(0, 10);
    }

    /**
     * Follower follows a followee. If the operation is invalid, it should be a no-op.
     */
    public void follow(int followerId, int followeeId) {
        if (followerId == followeeId) {
            return;
        }
        if (followMap.containsKey(followerId)) {
            followMap.get(followerId).add(followeeId);
        } else {
            List<Integer> followees = new LinkedList<>();
            followees.add(followeeId);
            followees.add(followerId);
            followMap.put(followerId, followees);
        }
    }

    /**
     * Follower unfollows a followee. If the operation is invalid, it should be a no-op.
     */
    public void unfollow(int followerId, int followeeId) {
        if (followerId == followeeId) {
            return;
        }
        if (followMap.containsKey(followerId)) {
            List<Integer> list = followMap.get(followerId);
            list.removeIf(x -> x == followeeId);
        }
    }

}

/**
 * Your Twitter object will be instantiated and called as such:
 * Twitter obj = new Twitter();
 * obj.postTweet(userId,tweetId);
 * List<Integer> param_2 = obj.getNewsFeed(userId);
 * obj.follow(followerId,followeeId);
 * obj.unfollow(followerId,followeeId);
 */

378. 有序矩阵中第 K 小的元素

class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int n = matrix.length;
        int left = matrix[0][0];
        int right = matrix[n - 1][n - 1];
        while (left < right) {
            int mid = left + ((right - left) >> 1);
            if (check(matrix, mid, k, n)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    public boolean check(int[][] matrix, int mid, int k, int n) {
        int i = n - 1;
        int j = 0;
        int num = 0;
        while (i >= 0 && j < n) {
            if (matrix[i][j] <= mid) {
                num += i + 1;
                j++;
            } else {
                i--;
            }
        }
        return num >= k;
    }
}
import scala.collection.mutable

object Solution {
  def kthSmallest(matrix: Array[Array[Int]], k: Int): Int = {
    val n = matrix.length
    val priorityQueue = new mutable.PriorityQueue[Int]()(Ordering.by(x => -x))
    for (i <- 0 until n; j <- 0 until n) {
      priorityQueue.enqueue(matrix(i)(j))
    }
    var index = 0
    while (index < k - 1) {
      priorityQueue.dequeue()
      index += 1
    }
    priorityQueue.dequeue()
  }
}

621. 任务调度器

class Solution {
    public int leastInterval(char[] tasks, int n) {
        Map<Character, Integer> freq = new HashMap<Character, Integer>();
        // 最多的执行次数
        int maxExec = 0;
        for (char ch : tasks) {
            int exec = freq.getOrDefault(ch, 0) + 1;
            freq.put(ch, exec);
            maxExec = Math.max(maxExec, exec);
        }

        // 具有最多执行次数的任务数量
        int maxCount = 0;
        Set<Map.Entry<Character, Integer>> entrySet = freq.entrySet();
        for (Map.Entry<Character, Integer> entry : entrySet) {
            int value = entry.getValue();
            if (value == maxExec) {
                ++maxCount;
            }
        }

        return Math.max((maxExec - 1) * (n + 1) + maxCount, tasks.length);
    }
}

659. 分割数组为连续子序列

class Solution {
    public boolean isPossible(int[] nums) {
        Map<Integer, Integer> countMap = new HashMap<Integer, Integer>();
        Map<Integer, Integer> endMap = new HashMap<Integer, Integer>();
        for (int x : nums) {
            int count = countMap.getOrDefault(x, 0) + 1;
            countMap.put(x, count);
        }
        for (int x : nums) {
            int count = countMap.getOrDefault(x, 0);
            if (count > 0) {
                int prevEndCount = endMap.getOrDefault(x - 1, 0);
                if (prevEndCount > 0) {
                    countMap.put(x, count - 1);
                    endMap.put(x - 1, prevEndCount - 1);
                    endMap.put(x, endMap.getOrDefault(x, 0) + 1);
                } else {
                    int count1 = countMap.getOrDefault(x + 1, 0);
                    int count2 = countMap.getOrDefault(x + 2, 0);
                    if (count1 > 0 && count2 > 0) {
                        countMap.put(x, count - 1);
                        countMap.put(x + 1, count1 - 1);
                        countMap.put(x + 2, count2 - 1);
                        endMap.put(x + 2, endMap.getOrDefault(x + 2, 0) + 1);
                    } else {
                        return false;
                    }
                }
            }
        }
        return true;
    }
}
class Solution {
    public boolean isPossible(int[] nums) {
        Map<Integer, PriorityQueue<Integer>> map = new HashMap<>();
        for (int x : nums) {
            if (!map.containsKey(x)) {
                map.put(x, new PriorityQueue<>());
            }
            if (map.containsKey(x - 1)) {
                int prevLength = map.get(x - 1).poll();
                if (map.get(x - 1).isEmpty()) {
                    map.remove(x - 1);
                }
                map.get(x).offer(prevLength + 1);
            } else {
                map.get(x).offer(1);
            }
        }
        for (Map.Entry<Integer, PriorityQueue<Integer>> entry : map.entrySet()) {
            PriorityQueue<Integer> queue = entry.getValue();
            if (queue.peek() < 3) {
                return false;
            }
        }
        return true;
    }
}

692. 前K个高频单词

class Solution {
    public List<String> topKFrequent(String[] words, int k) {
        Map<String, Long> map = Arrays.stream(words)
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        Set<String> set = new TreeSet<>((x1, x2) -> (int)(map.get(x1).equals(map.get(x2)) ?
                x1.compareTo(x2) :
                map.get(x2) - map.get(x1)));
        set.addAll(map.keySet());
        return new ArrayList<>(set).subList(0, k);
    }
}
class Solution {
    public List<String> topKFrequent(String[] words, int k) {
        Map<String, Integer> cnt = new HashMap<String, Integer>();
        for (String word : words) {
            cnt.put(word, cnt.getOrDefault(word, 0) + 1);
        }
        PriorityQueue<Map.Entry<String, Integer>> pq = new PriorityQueue<Map.Entry<String, Integer>>(new Comparator<Map.Entry<String, Integer>>() {
            public int compare(Map.Entry<String, Integer> entry1, Map.Entry<String, Integer> entry2) {
                return entry1.getValue() == entry2.getValue() ? entry2.getKey().compareTo(entry1.getKey()) : entry1.getValue() - entry2.getValue();
            }
        });
        for (Map.Entry<String, Integer> entry : cnt.entrySet()) {
            pq.offer(entry);
            if (pq.size() > k) {
                pq.poll();
            }
        }
        List<String> ret = new ArrayList<String>();
        while (!pq.isEmpty()) {
            ret.add(pq.poll().getKey());
        }
        Collections.reverse(ret);
        return ret;
    }
}

703. 数据流中的第 K 大元素

class KthLargest {

    final PriorityQueue<Integer> q;
    final int k;

    public KthLargest(int k, int[] a) {
        this.k = k;
        q = new PriorityQueue<>(k);
        for (int n : a) {
            add(n);
        }
    }

    public int add(int n) {
        if (q.size() < k) {
            q.offer(n);
        } else if (q.peek() < n) {
            q.poll();
            q.offer(n);
        }
        return q.peek();
    }
}


/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest obj = new KthLargest(k, nums);
 * int param_1 = obj.add(val);
 */

1046. 最后一块石头的重量

class Solution {
    public int lastStoneWeight(int[] stones) {
        PriorityQueue<Integer> pq = new PriorityQueue<Integer>((a, b) -> b - a);
        for (int stone : stones) {
            pq.offer(stone);
        }

        while (pq.size() > 1) {
            int a = pq.poll();
            int b = pq.poll();
            if (a > b) {
                pq.offer(a - b);
            }
        }
        return pq.isEmpty() ? 0 : pq.poll();
    }
}

1631. 最小体力消耗路径

class Solution {
    int[] DIR = new int[]{0, 1, 0, -1, 0};
    public int minimumEffortPath(int[][] heights) {
        int m = heights.length, n = heights[0].length;
        int[][] dist = new int[m][n];
        for (int i = 0; i < m; i++) Arrays.fill(dist[i], Integer.MAX_VALUE);
        
        PriorityQueue<int[]> minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
        minHeap.offer(new int[]{0, 0, 0}); // distance, row, col
        while (!minHeap.isEmpty()) {
            int[] top = minHeap.poll();
            int d = top[0], r = top[1], c = top[2];
            if (r == m - 1 && c == n - 1) return d;
            for (int i = 0; i < 4; i++) {
                int nr = r + DIR[i], nc = c + DIR[i + 1];
                if (nr >= 0 && nr < m && nc >= 0 && nc < n) {
                    int newDist = Math.max(d, Math.abs(heights[nr][nc] - heights[r][c]));
                    if (dist[nr][nc] > newDist) {
                        dist[nr][nc] = newDist;
                        minHeap.offer(new int[]{dist[nr][nc], nr, nc});
                    }
                }
            }
        }
        return 0;
    }
}
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值