合併兩個有序鏈表21

方法一:遞歸

思路

我們可以如下遞歸地定義兩個鏈表裏的 merge 操作(忽略邊界情況,比如空鏈表等):
也就是說,兩個鏈表頭部值較小的一個節點與剩下元素的 merge 操作結果合併。
算法
我們直接將以上遞歸過程建模,同時需要考慮邊界情況。
如果 l1 或者 l2 一開始就是空鏈表 ,那麼沒有任何操作需要合併,所以我們只需要返回非空鏈表。否則,我們要判斷 l1 和 l2 哪一個鏈表的頭節點的值更小,然後遞歸地決定下一個添加到結果裏的節點。如果兩個鏈表有一個爲空,遞歸結束。

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        else if (l2 == null) {
            return l1;
        }
        else if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        }
        else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }

    }
}

複雜度分析

時間複雜度:O(n + m,其中 n和 m 分別爲兩個鏈表的長度。因爲每次調用遞歸都會去掉 l1 或者 l2 的頭節點(直到至少有一個鏈表爲空),函數 mergeTwoList 至多隻會遞歸調用每個節點一次。因此,時間複雜度取決於合併後的鏈表長度,即 O(n+m)。

空間複雜度:O(n + m),其中 n 和 m 分別爲兩個鏈表的長度。遞歸調用 mergeTwoLists 函數時需要消耗棧空間,棧空間的大小取決於遞歸調用的深度。結束遞歸調用時 mergeTwoLists 函數最多調用 n+m 次,因此空間複雜度爲 O(n+m)

方法二:迭代

思路

我們可以用迭代的方法來實現上述算法。當 l1 和 l2 都不是空鏈表時,判斷 l1 和 l2 哪一個鏈表的頭節點的值更小,將較小值的節點添加到結果裏,當一個節點被添加到結果裏之後,將對應鏈表中的節點向後移一位。

算法

1. 首先,我們設定一個哨兵節點 prehead ,這可以在最後讓我們比較容易地返回合併後的鏈表。我們維護一個 prev 指針,我們需要做的是調整它的 next 指針。然後,我們重複以下過程,直到 l1 或者 l2 指向了 null :如果 l1 當前節點的值小於等於 l2 ,我們就把 l1 當前的節點接在 prev 節點的後面同時將 l1 指針往後移一位。否則,我們對 l2 做同樣的操作。不管我們將哪一個元素接在了後面,我們都需要把 prev 向後移一位。

2. 在循環終止的時候, l1 和 l2 至多有一個是非空的。由於輸入的兩個鏈表都是有序的,所以不管哪個鏈表是非空的,它包含的所有元素都比前面已經合併鏈表中的所有元素都要大。這意味着我們只需要簡單地將非空鏈表接在合併鏈表的後面,並返回合併鏈表即可。

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode prehead = new ListNode(-1);

        ListNode prev = prehead;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                prev.next = l1;
                l1 = l1.next;
            } else {
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }

        // 合併後 l1 和 l2 最多隻有一個還未被合併完,我們直接將鏈表末尾指向未合併完的鏈表即可
        prev.next = l1 == null ? l2 : l1;

        return prehead.next;
    }
}

複雜度分析

時間複雜度:O(n + m) ,其中 n 和 m 分別爲兩個鏈表的長度。因爲每次循環迭代中,l1 和 l2 只有一個元素會被放進合併鏈表中, 因此 while 循環的次數不會超過兩個鏈表的長度之和。所有其他操作的時間複雜度都是常數級別的,因此總的時間複雜度爲 O(n+m)。
空間複雜度:O(1) 。我們只需要常數的空間存放若干變量。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章