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语言的代码实现和测试用例