378. Kth Smallest Element in a Sorted Matrix

Difficulty:
Related Topics:
Similar Questions:

    Problem

    Given an n x n matrix where each of the rows and columns is sorted in ascending order, return the kth smallest element in the matrix.

    Note that it is the kth smallest element in the sorted order, not the kth distinct element.

    You must find a solution with a memory complexity better than O(n2).

    Example 1:

    Input: matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
    Output: 13
    Explanation: The elements in the matrix are [1,5,9,10,11,12,13,13,15], and the 8th smallest number is 13
    

    Example 2:

    Input: matrix = [[-5]], k = 1
    Output: -5
    

    Solution

    /**
     * @param {number[][]} matrix
     * @param {number} k
     * @return {number}
     */
    var kthSmallest = function(matrix, k) {
      const minHeap = Heap((a, b) => a.val - b.val)(
        matrix.map((arr, rowIndex) => ({
          val: arr[0],
          rowIndex,
          columnIndex: 0,
        })),
      )
      for (let i = 1; i < k; i++) {
        const { rowIndex, columnIndex } = minHeap.extract()
        if (matrix[rowIndex][columnIndex + 1] !== undefined) {
          minHeap.add({
            val: matrix[rowIndex][columnIndex + 1],
            rowIndex,
            columnIndex: columnIndex + 1,
          })
        }
      }
      return minHeap.extract().val
    };
    
    const swap = (a, b, arr) => { // eslint-disable-line
      if (a !== b) {
        const temp = arr[a]
        arr[a] = arr[b] // eslint-disable-line
        arr[b] = temp // eslint-disable-line
      }
    }
    
    const Heap = compareFn => (arr = []) => {
      const left = index => 2 * index + 1
      const right = index => 2 * index + 2
      const parent = index => Math.floor((index - 1) / 2)
      const size = () => arr.length
    
      // log(n)
      const heapify = (index) => {
        const l = left(index)
        const r = right(index)
        let current = index
        if ((l < size()) && compareFn(arr[current], arr[l]) > 0) {
          current = l
        }
        if ((r < size()) && compareFn(arr[current], arr[r]) > 0) {
          current = r
        }
        if (current !== index) {
          swap(current, index, arr)
          heapify(current)
        }
      }
      // log(n)
      const heapifyUp = (index) => {
        const p = parent(index)
        if (p >= 0 && compareFn(arr[p], arr[index]) > 0) {
          swap(p, index, arr)
          heapifyUp(p)
        }
      }
      // O(n)
      const buildHeap = () => {
        for (let i = Math.floor(arr.length / 2); i >= 0; i--) {
          heapify(i)
        }
      }
      const extract = () => {
        swap(0, arr.length - 1, arr)
        const top = arr.pop()
        heapify(0)
        return top
      }
      const remove = (item) => {
        const index = arr.findIndex(x => compareFn(x, item) === 0)
        if (index === -1) {
          return
        }
        arr[index] = arr.pop() // eslint-disable-line
        const p = parent(index)
        if (p < 0 || compareFn(p, arr[index]) < 0) {
          heapify(index)
        } else {
          heapifyUp(index)
        }
      }
      buildHeap()
      return {
        getHeap: () => arr,
        peek: () => {
          if (arr.length === 0) {
            return null
          }
          return arr[0]
        },
        add: (item) => {
          arr.push(item)
          heapifyUp(arr.length - 1)
        },
        extract,
        remove,
        size,
      }
    }
    

    Complexity: