LeetCode 合并K个升序链表

99 阅读3分钟

LeetCode 合并K个升序链表

合并K个升序链表是一道非常经典的算法题,也是面试中经常被问到的一个考点。本文将重点介绍该题的解决思路和代码实现,帮助读者更好地掌握该算法。

一、题目描述

合并K个升序链表,返回合并后的链表。例如,如果有3个升序链表分别为[1->4->5, 1->3->4, 2->6],则合并后的链表为1->1->2->3->4->4->5->6。

二、解题思路

解决该问题的思路是采用分治法,并借助归并排序的思想。具体来说,将K个链表两两合并成K/2个链表,每个新链表长度为原链表长度的两倍。如此重复进行,直至最终合并成一个链表。

三、代码实现

我们先实现一个辅助函数merage(L1, L2)用于合并两个链表L1和L2。函数的实现思路是利用双指针,将两个链表归并到一起,最终返回合并后的链表头指针。

public ListNode merge(ListNode l1, ListNode l2) {
    ListNode dummy = new ListNode(-1);
    ListNode tail = dummy;
    while (l1 != null && l2 != null) {
        if (l1.val < l2.val) {
            tail.next = l1;
            l1 = l1.next;
        } else {
            tail.next = l2;
            l2 = l2.next;
        }
        tail = tail.next;
    }
    tail.next = (l1 != null) ? l1 : l2;
    return dummy.next;
}

接下来,我们需要实现一个递归函数merageKLists(lists, l, r),用于分治合并K个链表。该函数具体实现如下:

public ListNode mergeKLists(ListNode[] lists) {
    if (lists == null || lists.length == 0) {
        return null;
    }
    return mergeKLists(lists, 0, lists.length - 1);
}

private ListNode mergeKLists(ListNode[] lists, int l, int r) {
    if (l == r) {
        return lists[l];
    }
    if (l > r) {
        return null;
    }
    int mid = l + (r - l) / 2;
    ListNode l1 = mergeKLists(lists, l, mid);
    ListNode l2 = mergeKLists(lists, mid + 1, r);
    return merge(l1, l2);
}

该函数的实现思路是首先判断特殊情况,即lists为空或长度为0时,直接返回null;如果l等于r,表示lists中只有一个链表,直接返回该链表;否则,递归地分治lists的左半部分和右半部分,合并左右两个链表即可。

四、代码测试 测试用例涵盖了各种情况。

public class MergeKListsTest {

    private MergeKLists mergeKLists;

    @Before
    public void setUp() {
        mergeKLists = new MergeKLists();
    }

    @After
    public void tearDown() {
        mergeKLists = null;
    }

    @Test
    public void testNull() {
        ListNode[] lists = null;
        ListNode result = mergeKLists.mergeKLists(lists);
        assertNull(result);
    }

    @Test
    public void testEmpty() {
        ListNode[] lists = new ListNode[0];
        ListNode result = mergeKLists.mergeKLists(lists);
        assertNull(result);
    }

    @Test
    public void testOneList() {
        ListNode l1 = buildList(new int[]{1, 4, 5});
        ListNode[] lists = new ListNode[]{l1};
        ListNode result = mergeKLists.mergeKLists(lists);
        assertListEquals(l1, result);
    }

    @Test
    public void testTwoLists() {
        ListNode l1 = buildList(new int[]{1, 4, 5});
        ListNode l2 = buildList(new int[]{1, 3, 4});
        ListNode[] lists = new ListNode[]{l1, l2};
        ListNode expected = buildList(new int[]{1, 1, 3, 4, 4, 5});
        ListNode result = mergeKLists.mergeKLists(lists);
        assertListEquals(expected, result);
    }

    @Test
    public void testThreeLists() {
        ListNode l1 = buildList(new int[]{1, 4, 5});
        ListNode l2 = buildList(new int[]{1, 3, 4});
        ListNode l3 = buildList(new int[]{2, 6});
        ListNode[] lists = new ListNode[]{l1, l2, l3};
        ListNode expected = buildList(new int[]{1, 1, 2, 3, 4, 4, 5, 6});
    ListNode result = mergeKLists.mergeKLists(lists);
    assertListEquals(expected, result);
}

private ListNode buildList(int[] nums) {
    ListNode dummy = new ListNode(-1);
    ListNode tail = dummy;
    for (int num : nums) {
        tail.next = new ListNode(num);
        tail = tail.next;
    }
    return dummy.next;
}

private void assertListEquals(ListNode expected, ListNode actual) {
    while (expected != null && actual != null) {
        assertEquals(expected.val, actual.val);
        expected = expected.next;
        actual = actual.next;
    }
    assertNull(expected);
    assertNull(actual);
}


}

在测试用例中,我们通过buildList函数构造一个给定数据的链表,并且通过assertListEquals函数验证预期结果和实际结果是否相同。

五、总结

合并K个升序链表是一道比较典型的分治算法应用,而分治算法则是一种非常重要的算法思想,需要掌握并熟练运用。本文通过合并K个升序链表的例子来介绍分治算法的具体实现方法,同时还提供了Java语言的代码实现和测试用例