概述
合并两个有序的单链表为一个链表,原题目链接:LeetCode--21. Merge Two Sorted Lists
原题目:
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
Example:
Input: 1->2->4, 1->3->4 Output: 1->1->2->3->4->4
分析:本题应该是比较简单的,直接按照大小顺序合并两个链表就好了,需要注意的是,这里返回的新链表的头结点肯定是输入的两个链表中较小的一个,一般不确定最终返回链表的头结点时,我们会使用一个不包含任何数据,next指向真正的头结点的“虚结点”,有些书上也把这种结构称为带表头的链表。好处就是可以像处理其他结点一样来处理头结点,(我们知道在处理头结点的时候需要特别小心,在头结点出插入删除元素都需要改变头结点,而在其他位置插入,删除结点是不需要改变头结点的)
下面看AC代码:
public ListNode mergeTwoLists0(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0); // 表头节点,next域指向真正的头结点
ListNode cur = dummy;
while (l1 != null && l2 != null) {
cur.next = l1.val < l2.val ? l1 : l2;
cur = cur.next;
if (l1.val < l2.val) {
l1 = l1.next;
} else {
l2 = l2.next;
}
}
cur.next = l1 != null ? l1 : l2;
return dummy.next;
}
本题还有更简洁的递归版本,需要大家好好理解下,有时候递归能写出特别漂亮的程序,就是不好想!
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
分析完了合并两个链表,下面我们来看合并K个有序链表一个怎样操作,LeetCode--23. Merge k Sorted Lists
原题目:
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Example:
Input: [ 1->4->5, 1->3->4, 2->6 ] Output: 1->1->2->3->4->4->5->6
本题是上一题的扩展,需要合并k个有序的链表。
分析:最为直观的解法就是两两合并,最后合并成一个大链表了,下面是AC代码:
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
ListNode res = null;
for (ListNode l : lists) {
res = mergeTwoLists(l, res);
}
return res;
}
其中,mergeTwoLists方法就是上面合并两个链表的方法。
这里我们假设链表的个数为K个,K个链表包含的结点个数和为N。
使用上诉方法,很容易的知道,每个结点被访问的平均次数为 K/2,所以时间复杂度为O(NK),空间复杂度为O(1)。
接着来分析:由于每一次我们只需要比较K个链表的第一个结点,选出最小的哪一个作为新链表的下一个结点,所以我们可以使用一个堆来维护这个性质,下面是AC代码:
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
PriorityQueue<ListNode> pq = new PriorityQueue<>((n1, n2) -> n1.val - n2.val);
ListNode dummy = new ListNode(0); // 表头结点
ListNode cur = dummy;
for (ListNode node : lists)
if (node != null)
pq.add(node);
while (!pq.isEmpty()) {
cur.next = pq.poll();
cur = cur.next;
if (cur.next != null) {
pq.add(cur.next);
}
}
return dummy.next;
}
其中也用到了表头结点。
堆中最大元素数量为k个,每次调整时时间复杂度为O(lgK),总共有N个元素,所以时间复杂度为O(NlgK),空间复杂度为O(K)。
接着分析:对于上诉的两两合并,我们还可以进一步的改进,我们以K=8(l1,l2,l3,l4,l5,l6,l7,l8)为例:
在两两合并的方法中:第一趟:合并 l1 + l2
第二趟:合并 l1 + l2 + l3
第三趟:合并 l1 + l2 + l3 + l4
第四趟:合并 l1 + l2 + l3 + l4 + l5
第五趟:合并 l1 + l2 +l3 + l4 + l5 + l6
第六趟:合并 l1 + l2 +l3 + l4 + l5 + l6 + l7
第七趟:合并 l1 + l2 +l3 + l4 + l5 + l6 + l7 + l8
改进:第一趟,合并 l1 + l2 l3 + l4 l5 + l6 l7 + l8
第二趟,合并l1 + l2 + l3 + l4 l5 + l6 + l7 + l8
第三趟,合并 l1 + l2 +l3 + l4 + l5 + l6 + l7 + l8
可以看到在改进的方法中,每个元素被访问的次数为O(lgK),所以时间复杂度为O(NlgK),空间复杂度为O(1).
下面是AC代码:
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
return mergeKLists(lists, 0, lists.length);
}
private ListNode mergeKLists(ListNode[] lists, int start, int stop) {
if (start + 1 == stop) return lists[start];
return mergeTwoLists(mergeKLists(lists, start, (start + stop) / 2),
mergeKLists(lists, (start + stop) / 2, stop));
}
好了,我们下期见!!!
最后
以上就是辛勤银耳汤为你收集整理的LeetCode--合并有序链表总结的全部内容,希望文章能够帮你解决LeetCode--合并有序链表总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复