23. 合并 K 个升序链表

54 阅读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

示例 2:

输入: lists = []
输出: []

示例 3:

输入: lists = [[]]
输出: []

 

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4

思路 1

使用堆排序,先建好小根堆,将第一轮节点压入堆,弹出最小节点插入新链表中;

如果最小节点还有后续节点,继续压入堆,然后继续弹出;

重复上述操作,直至堆的大小为空位置。

代码

type myHeap []*ListNode

func (mh myHeap) Len() int {
    return len(mh)
}

func (mh myHeap) Swap(i, j int) {
    mh[i], mh[j] = mh[j], mh[i]
}

func (mh myHeap) Less(i, j int) bool {
    return mh[i].Val < mh[j].Val
}

func (mh *myHeap) Push(x any) {
    *mh = append(*mh, x.(*ListNode))
}

func (mh *myHeap) Pop() any {
    x := (*mh)[mh.Len()-1]
    *mh = (*mh)[:mh.Len()-1]
    return x
}

func mergeKLists(lists []*ListNode) *ListNode {
    var dummy, tail, node *ListNode
    dummy = new(ListNode)
    tail = dummy

    h := myHeap{}
    heap.Init(&h)
    for _, list := range lists {
        if list == nil {
            continue
        }
        heap.Push(&h, list)
    }
    
    for h.Len() > 0 {
        node = heap.Pop(&h).(*ListNode)
        tail.Next = node
        tail = tail.Next
        if node.Next != nil {
            heap.Push(&h, node.Next)
        }
    }

    return dummy.Next
}

图示

image.png

思路 2

暴力解决:保证 lists 中没有 nil 节点,然后每次遍历找最小节点下标,使用尾插法插入到新的链表尾部。然后移动该节点并判断是否为 nil,如果是则从 lists 中移除,当 lists 为空时,处理结束。

1、将非 nil 节点加入到新的 lists 切片中

2、找到本轮最小值和所在的下标,插入到新链表的末尾

3、更新 lists 数组

4、返回新链表头结点(哑节点之后第一个节点)

代码

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func mergeKLists(lists []*ListNode) *ListNode {
    dummy := &ListNode{}
    tail := dummy
    
    newList := []*ListNode{}
    for i := 0; i < len(lists); i++ {
        if lists[i] != nil {
            newList = append(newList, lists[i])
        }
    }
    
    for len(newList) != 0 {
        target, index := newList[0], 0
        for i := 1; i < len(newList); i++ {
            if newList[i].Val < target.Val {
                target = newList[i]
                index = i
            }
        }
        tail.Next = target
        tail = tail.Next

        newList[index] = newList[index].Next
        if newList[index] == nil {
            newList = append(newList[:index], newList[index+1:]...)
        }
    }

    return dummy.Next
}

思路3

暴力算力最坏情况下空间复杂度达到了 O(n),使用下面方法可以使用常数级别空间复杂度即可解决。

  1. 使用哑节点和尾插法重新构造新链表
  2. 两个辅助变量,分别用来存储本轮最小的元素值和本轮最小元素值在lists数组中的索引
  3. 将最小元素值使用尾插法插入到新链表尾部并更新lists链表

代码

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func mergeKLists(lists []*ListNode) *ListNode {
    var dummy, tail *ListNode
    var minValue, minIndex int
    dummy = &ListNode{}
    tail = dummy

    for {
        minValue, minIndex = 10001, 0
        for idx, list := range lists {
            if list == nil {
                continue
            }
            if list.Val < minValue {
                minValue = list.Val
                minIndex = idx
            }
        }
        if minValue == 10001 {
            break
        }
        tail.Next = lists[minIndex]
        tail = tail.Next
        lists[minIndex] = lists[minIndex].Next
    }
    
    return dummy.Next
}

图示

image.png