合并两个已排序的单链表

时间:2017-03-29 14:27:16

标签: java algorithm merge linked-list

在寻找一些考试练习时,我在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%慢。我最近对链接列表很不满意,而且我对这个概念仍然不太满意,这就是为什么理解上可能存在一些失误导致代码效率低下的原因。如果有人能解释如何改进我的实施,我将不胜感激。

1 个答案:

答案 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