在寻找一些考试练习时,我在Leetcode上遇到了这个问题,我提出了以下解决方案:
public class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
public class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
// base case: if any one of the lists are empty then we are done.
if (l1 == null) return l2;
else if (l2 == null) return l1;
ListNode head = new ListNode(-1);
// dummy head node
ListNode prev = head; // pointer to do the modifications on the list
while ((l1 != null) && (l2 != null)) {
// while both lists arent empty
int val;
if (l1.val < l2.val) {
val = l1.val;
l1 = l1.next;
} else {
val = l2.val;
l2 = l2.next;
}
ListNode curr = new ListNode(val); // creates a new node with the chosen value
prev.next = curr; // update pointers
prev = curr;
}
// one of the list is finished. we add the rest onto the list ln
if (l1 == null) prev.next = l2;
else prev.next = l1;
return head.next;
}
}
它似乎通过了所有测试 - 但我遇到的问题是它比提交的解决方案的90%慢。我最近对链接列表很不满意,而且我对这个概念仍然不太满意,这就是为什么理解上可能存在一些失误导致代码效率低下的原因。如果有人能解释如何改进我的实施,我将不胜感激。
答案 0 :(得分:0)
您的代码存在两个问题。
首先,它与输入数据的工作方式不一致。如果其中一个输入列表为空,则例程返回另一个输入列表。另一方面,如果两个输入列表都是非空的,则例程会创建新项以构建结果列表。但是,当其中一个输入列表在合并过程中耗尽时,另一个的剩余部分将附加到结果列表中。最终,结果列表部分使用新节点和其中一个输入列表的尾部构建。
这会导致创建重复节点和销毁原始节点的不必要开销,而原始节点已被替换为副本。
为避免这种情况,请直接使用从输入列表中获取的节点来建立结果列表。然后,没有创建新节点,没有旧节点的回收,没有中间变量在复制之前保留val
,如果ListNode
类是l1
类,则不会有忘记复制其他数据成员的风险永远延长......
另一个是对输出节点进行排序。你使用&#39;少于&#39;运算符决定是从l2
还是从l1
获取下一个节点。那是错的。您应该使用&#39;小于或等于&#39;,因此对于相等的键,您首先从l2
获取节点,然后从val
获取节点。这样,具有相等键值public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode head = new ListNode(-1); // temporary head node
ListNode last = head; // the last item of the list; we append here
while ((l1 != null) && (l2 != null)) { // while both lists arent empty
if (l1.val <= l2.val) { // 'or equal' for stable merging
last.next = l1; // append the chosen node to result list
l1 = l1.next; // skip (effectively remove) it in input list
} else {
last.next = l2;
l2 = l2.next;
}
last = last.next; // the appended node becomes last
}
// one of the lists is finished - append the rest of the other one
if (l1 == null) last.next = l2;
else last.next = l1;
return head.next;
}
但是以其他方式区分(例如,它们具有其他数据成员)的节点保持原始顺序。我们称之为稳定排序。
实现:
while
此外,我们不需要测试任何输入列表是否为空 - 如果是,则跳过if
循环而不进行迭代,最后if
附加非空输入列表结果。此处的增益仅为创建临时head
节点的成本ListNode
一个。
我们也可以以额外的条件分支为代价去掉临时public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
else if (l2 == null) return l1; // make sure both lists aren't empty
ListNode head = null; // the resulting list's head
if (l1.val <= l2.val) { // choose the head node
head = l1;
l1 = l1.next;
} else {
head = l2;
l2 = l2.next;
}
ListNode last = head; // the last item of the list; we append here
while ((l1 != null) && (l2 != null)) { // while both lists arent empty
if (l1.val <= l2.val) { // 'or equal' for stable merging
last.next = l1; // append the chosen node to result list
l1 = l1.next; // skip (effectively remove) it in input list
} else {
last.next = l2;
l2 = l2.next;
}
last = last.next; // the appended node becomes last
}
// one of the lists is finished - append the rest of the other one
if (l1 == null) last.next = l2;
else last.next = l1;
return head;
}
变量。当节点类变得更复杂时,这可能很有用,因为我们可能需要为构造函数提供更多参数,这可能在合并例程中不可用。
toy