23. 合并K个升序链表

425 阅读2分钟

题目描述

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

解题思路

算法

多路归并,小顶堆

思路

我们用小顶堆来维护存储最小值的链表头节点

先将所有的链表头节点 insert(填充) 堆中

这边需要注意一点,我们维护的是小顶堆,填充的是链表节点,对比用的对比函数比较的是节点值

然后不断地 extract(弹出)头节点,将他的合并到新链表(ret

在合并之后,我们需要看弹出的头节点 cur 是否存在 cur.next,也就是还有没有后边继元素,如果有的话需要 insert 堆中,然后只要堆中还有元素,这个过程就要一直继续下去

最终返回新链表的头节点 ret.next

代码

/**
       * Definition for singly-linked list.
       * function ListNode(val, next) {
       *     this.val = (val===undefined ? 0 : val)
       *     this.next = (next===undefined ? null : next)
       * }
       */
      /**
       * @param {ListNode[]} lists
       * @return {ListNode}
       */

      // 链表,堆,归并排序

      function ListNode(val, next) {
        this.val = val === undefined ? 0 : val
        this.next = next === undefined ? null : next
      }
      const l1 = new ListNode(1, new ListNode(4, new ListNode(5)))
      const l2 = new ListNode(1, new ListNode(3, new ListNode(4)))
      const l3 = new ListNode(2, new ListNode(6))

      var mergeKLists = function (lists) {
        // 优先队列存储当前所有 k 个链表当前位置
        const h = new Heap((a, b) => {
          if (!b) return false
          return a.val > b.val
        })

        // 将 k 个链表的 head 依次押入队列
        for (x of lists) {
          if (!x) continue
          h.insert(x)
        }

        // 从 h 不断弹出节点,并且放到合并链表的末尾
        let ret = new ListNode(),
          p = ret
        while (!h.isEmpty()) {
          const cur = h.extract()
          p.next = cur
          p = p.next

          // 如果存在 cur.next,即当前弹出的节点有 next 节点,我们需要把它放入 h
          if (cur.next) {
            h.insert(cur.next)
          }
        }

        return ret.next
      }

      class Heap {
        constructor(compareFn) {
          this.compareFn = compareFn
          this.heap = []
        }

        swap(parent, index) {
          const arr = this.heap
          ;[arr[parent], arr[index]] = [arr[index], arr[parent]]
        }

        getLeftIndex(index) {
          return index * 2 + 1
        }

        getRightIndex(index) {
          return index * 2 + 2
        }

        getParentIndex(index) {
          return Math.floor((index - 1) / 2)
        }

        size() {
          return this.heap.length
        }

        isEmpty() {
          return this.size() === 0
        }

        insert(value) {
          const index = this.heap.length
          this.heap.push(value)

          this.siftUp(index)
        }

        siftUp(index) {
          let parent = this.getParentIndex(index)

          while (
            index > 0 &&
            this.compareFn(this.heap[parent], this.heap[index])
          ) {
            this.swap(parent, index)
            index = parent
            parent = this.getParentIndex(index)
          }
        }

        extract() {
          if (this.isEmpty()) return
          if (this.size() === 1) return this.heap.pop()

          const removedItem = this.heap[0]
          this.heap[0] = this.heap.pop()
          this.siftDown(0)

          return removedItem
        }

        siftDown(index) {
          let element = index
          const left = this.getLeftIndex(index)
          const right = this.getRightIndex(index)

          if (
            index < this.size() &&
            this.compareFn(this.heap[element], this.heap[left])
          ) {
            element = left
          }

          if (
            index < this.size() &&
            this.compareFn(this.heap[element], this.heap[right])
          ) {
            element = right
          }

          if (element !== index) {
            this.swap(element, index)
            this.siftDown(element)
          }
        }

        top() {
          return this.heap[0]
        }
      }