Skip to content

Heap Advanced

Table of Contents

23. Merge k Sorted Lists

  • LeetCode | 力扣

  • Tags: Linked List, Divide And Conquer, Heap Priority Queue, Merge Sort

"""
-   Prerequisite: 21. Merge Two Sorted Lists
-   Video explanation: [23. Merge K Sorted Lists - NeetCode](https://youtu.be/q5a5OiGbT6Q?si=SQ2dCvsYQ3LQctPh)
"""

import copy
import heapq
from typing import List, Optional

from leetpattern.utils import ListNode, list_from_array, list_to_array


def merge_k_lists_divide_conquer(
    lists: List[Optional[ListNode]],
) -> Optional[ListNode]:
    if not lists or len(lists) == 0:
        return None

    def mergeTwo(l1, l2):
        dummy = ListNode()
        cur = dummy

        while l1 and l2:
            if l1.val < l2.val:
                cur.next = l1
                l1 = l1.next
            else:
                cur.next = l2
                l2 = l2.next

            cur = cur.next

        cur.next = l1 if l1 else l2

        return dummy.next

    while len(lists) > 1:
        merged = []

        for i in range(0, len(lists), 2):
            l1 = lists[i]
            l2 = lists[i + 1] if i + 1 < len(lists) else None
            merged.append(mergeTwo(l1, l2))

        lists = merged

    return lists[0]


def merge_k_lists_heap(lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    dummy = ListNode()
    cur = dummy

    min_heap = []  # (val, idx, node)

    for idx, head in enumerate(lists):
        if head:
            heapq.heappush(min_heap, (head.val, idx, head))

    while min_heap:
        _, idx, node = heapq.heappop(min_heap)
        cur.next = node
        cur = cur.next

        node = node.next
        if node:
            heapq.heappush(min_heap, (node.val, idx, node))

    return dummy.next


def test_merge_k_lists() -> None:
    n1 = list_from_array([1, 4])
    n2 = list_from_array([1, 3])
    n3 = list_from_array([2, 6])
    lists = [n1, n2, n3]
    lists1 = copy.deepcopy(lists)
    lists2 = copy.deepcopy(lists)
    assert (list_to_array(merge_k_lists_divide_conquer(lists1))) == [
        1,
        1,
        2,
        3,
        4,
        6,
    ]
    assert (list_to_array(merge_k_lists_heap(lists2))) == [1, 1, 2, 3, 4, 6]

355. Design Twitter

  • LeetCode | 力扣

  • Tags: Hash Table, Linked List, Design, Heap Priority Queue

"""
-   Similar question: [23. Merge K Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/) (Hard)
"""

import heapq
from collections import defaultdict
from typing import List


# Design
class Twitter:

    def __init__(self):
        self.tweets = defaultdict(list)
        self.followees = defaultdict(set)
        self.time = 0

    def postTweet(self, userId: int, tweetId: int) -> None:
        self.tweets[userId].append((self.time, tweetId))
        self.time += 1

    def getNewsFeed(self, userId: int) -> List[int]:
        news_feed = []
        news_feed.extend(self.tweets[userId])
        for followee in self.followees[userId]:
            news_feed.extend(self.tweets[followee])

        return [tweet for _, tweet in heapq.nlargest(10, news_feed)]

    def follow(self, followerId: int, followeeId: int) -> None:
        if followerId != followeeId:
            self.followees[followerId].add(followeeId)

    def unfollow(self, followerId: int, followeeId: int) -> None:
        if followerId != followeeId:
            self.followees[followerId].discard(followeeId)


twitter = Twitter()
print(twitter.postTweet(1, 5))  # None
print(twitter.getNewsFeed(1))  # [5]
print(twitter.follow(1, 2))  # None
print(twitter.postTweet(2, 6))  # None
print(twitter.getNewsFeed(1))  # [6, 5]
print(twitter.unfollow(1, 2))  # None
print(twitter.getNewsFeed(1))  # [5]

502. IPO

import heapq
from typing import List


# Heap - Two Heaps
def findMaximizedCapital(k: int, w: int, profits: List[int], capital: List[int]) -> int:
    """
    Time Complexity: O(k log N)
    Space Complexity: O(N)
    """
    if not profits or not capital:
        return w

    if w >= max(capital) and k >= len(capital):
        return sum(profits) + w

    max_profit = []
    min_capital = [(c, p) for c, p in zip(capital, profits)]
    heapq.heapify(min_capital)

    for _ in range(k):
        while min_capital and min_capital[0][0] <= w:
            _, pro = heapq.heappop(min_capital)
            heapq.heappush(max_profit, -pro)

        if max_profit:
            w += -heapq.heappop(max_profit)

    return w


if __name__ == "__main__":
    k = 2
    w = 0
    profits = [1, 2, 3]
    capital = [0, 1, 1]
    assert findMaximizedCapital(k, w, profits, capital) == 4

1705. Maximum Number of Eaten Apples

778. Swim in Rising Water

  • LeetCode | 力扣

  • Tags: Array, Binary Search, Depth First Search, Breadth First Search, Union Find, Heap Priority Queue, Matrix

"""
-   Return the minimum time when you can reach the target.

![778](https://assets.leetcode.com/uploads/2021/06/29/swim2-grid-1.jpg)
"""

import heapq
from typing import List


# Dijkstra's
def swimInWater(grid: List[List[int]]) -> int:
    n = len(grid)
    visited = set()
    minHeap = [(grid[0][0], 0, 0)]
    directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]

    visited.add((0, 0))

    while minHeap:
        time, r, c = heapq.heappop(minHeap)

        if r == n - 1 and c == n - 1:
            return time

        for dr, dc in directions:
            nr, nc = r + dr, c + dc

            if nr in range(n) and nc in range(n) and (nr, nc) not in visited:
                visited.add((nr, nc))
                heapq.heappush(minHeap, (max(time, grid[nr][nc]), nr, nc))


grid = [
    [0, 1, 2, 3, 4],
    [24, 23, 22, 21, 5],
    [12, 13, 14, 15, 16],
    [11, 17, 18, 19, 20],
    [10, 9, 8, 7, 6],
]
print(swimInWater(grid))  # 16

1631. Path With Minimum Effort

  • LeetCode | 力扣

  • Tags: Array, Binary Search, Depth First Search, Breadth First Search, Union Find, Heap Priority Queue, Matrix

"""
-   Return the minimum effort required to travel from the top-left to the bottom-right corner.
"""

import heapq
from typing import List


# Prim
def minimumEffortPath(heights: List[List[int]]) -> int:
    m, n = len(heights), len(heights[0])
    directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
    visited = [[False] * n for _ in range(m)]
    heap = [(0, 0, 0)]  # (effort, row, col)

    while heap:
        effort, r, c = heapq.heappop(heap)

        if visited[r][c]:
            continue

        if r == m - 1 and c == n - 1:
            return effort

        visited[r][c] = True

        for dr, dc in directions:
            nr, nc = r + dr, c + dc

            if 0 <= nr < m and 0 <= nc < n and not visited[nr][nc]:
                updated = max(effort, abs(heights[r][c] - heights[nr][nc]))
                heapq.heappush(heap, (updated, nr, nc))

    return -1


heights = [[1, 2, 2], [3, 8, 2], [5, 3, 5]]
print(minimumEffortPath(heights))  # 2

1354. Construct Target Array With Multiple Sums

1353. Maximum Number of Events That Can Be Attended

1235. Maximum Profit in Job Scheduling

  • LeetCode | 力扣

  • Tags: Array, Binary Search, Dynamic Programming, Sorting

632. Smallest Range Covering Elements from K Lists

  • LeetCode | 力扣

  • Tags: Array, Hash Table, Greedy, Sliding Window, Sorting, Heap Priority Queue

from heapq import heapify, heapreplace
from math import inf
from typing import List


# Heap
def smallestRange(nums: List[List[int]]) -> List[int]:
    heap = [(arr[0], i, 0) for i, arr in enumerate(nums)]
    heapify(heap)

    res_l = heap[0][0]
    res_r = right = max(arr[0] for arr in nums)

    while heap[0][2] + 1 < len(nums[heap[0][1]]):
        _, i, j = heap[0]
        x = nums[i][j + 1]
        heapreplace(heap, (x, i, j + 1))
        right = max(right, x)
        left = heap[0][0]
        if right - left < res_r - res_l:
            res_l, res_r = left, right

    return [res_l, res_r]


# Sliding Window Variable Min
def smallestRangeSliding(nums: List[List[int]]) -> List[int]:
    pairs = sorted((x, i) for (i, arr) in enumerate(nums) for x in arr)
    res_l, res_r = -inf, inf
    empty = len(nums)
    cnt = [0] * empty
    left = 0

    for r, i in pairs:
        if cnt[i] == 0:
            empty -= 1
        cnt[i] += 1
        while empty == 0:
            l, i = pairs[left]
            if r - l < res_r - res_l:
                res_l, res_r = l, r
            cnt[i] -= 1
            if cnt[i] == 0:
                empty += 1
            left += 1

    return [res_l, res_r]


if __name__ == "__main__":
    nums = [[4, 10, 15, 24, 26], [0, 9, 12, 20], [5, 18, 22, 30]]
    assert smallestRange(nums) == [20, 24]
    assert smallestRangeSliding(nums) == [20, 24]

2542. Maximum Subsequence Score

1383. Maximum Performance of a Team

2503. Maximum Number of Points From Grid Queries

  • LeetCode | 力扣

  • Tags: Array, Two Pointers, Breadth First Search, Union Find, Sorting, Heap Priority Queue, Matrix

2163. Minimum Difference in Sums After Removal of Elements

  • LeetCode | 力扣

  • Tags: Array, Dynamic Programming, Heap Priority Queue

857. Minimum Cost to Hire K Workers

1606. Find Servers That Handled Most Number of Requests

  • LeetCode | 力扣

  • Tags: Array, Greedy, Heap Priority Queue, Ordered Set

1851. Minimum Interval to Include Each Query

  • LeetCode | 力扣

  • Tags: Array, Binary Search, Line Sweep, Sorting, Heap Priority Queue

218. The Skyline Problem

  • LeetCode | 力扣

  • Tags: Array, Divide And Conquer, Binary Indexed Tree, Segment Tree, Line Sweep, Heap Priority Queue, Ordered Set

407. Trapping Rain Water II

  • LeetCode | 力扣

  • Tags: Array, Breadth First Search, Heap Priority Queue, Matrix

2940. Find Building Where Alice and Bob Can Meet

  • LeetCode | 力扣

  • Tags: Array, Binary Search, Stack, Binary Indexed Tree, Segment Tree, Heap Priority Queue, Monotonic Stack

3399. Smallest Substring With Identical Characters II

2589. Minimum Time to Complete All Tasks

  • LeetCode | 力扣

  • Tags: Array, Binary Search, Stack, Greedy, Sorting

3266. Final Array State After K Multiplication Operations II

1675. Minimize Deviation in Array

  • LeetCode | 力扣

  • Tags: Array, Greedy, Heap Priority Queue, Ordered Set

2617. Minimum Number of Visited Cells in a Grid

  • LeetCode | 力扣

  • Tags: Array, Dynamic Programming, Stack, Breadth First Search, Union Find, Heap Priority Queue, Matrix, Monotonic Stack

2532. Time to Cross a Bridge

1199. Minimum Time to Build Blocks 👑