Leetcode-鏈表(一)

目錄

206. 反轉鏈表

92. 反轉鏈表 II

83. 刪除排序鏈表中的重複元素

82. 刪除排序鏈表中的重複元素 II

86. 分隔鏈表

328. 奇偶鏈表

2. 兩數相加

445. 兩數相加 II

203. 移除鏈表元素

21. 合併兩個有序鏈表


206. 反轉鏈表

https://leetcode-cn.com/problems/reverse-linked-list/

反轉一個單鏈表。

示例:

輸入: 1->2->3->4->5->NULL
輸出: 5->4->3->2->1->NULL
進階:
你可以迭代或遞歸地反轉鏈表。你能否用兩種方法解決這道題?

題解

一:迭代,定義了三個指針,通過挪動三個指針來完成題目。

class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        pre_node, cur_node = None, head
        while cur_node:
            next_node = cur_node.next
            cur_node.next = pre_node
            pre_node = cur_node
            cur_node = next_node
        return pre_node

二:迭代,copy官方題解,https://leetcode-cn.com/problems/reverse-linked-list/solution/fan-zhuan-lian-biao-by-leetcode/

遞歸版本稍微複雜一些,其關鍵在於反向工作。假設列表的其餘部分已經被反轉,現在我該如何反轉它前面的部分?

假設列表爲:

n_1 \rightarrow \cdots \rightarrow n_{k-1} \rightarrow n_k \rightarrow n_{k+1} \rightarrow \cdots \rightarrow n_m \rightarrow \oslash

若從節點n_{k+1}n_m已經被反轉,而我們正處於n_k

n_1 \rightarrow \cdots \rightarrow n_{k-1} \rightarrow n_k \rightarrow n_{k+1} \leftarrow \cdots \leftarrow n_m

我們希望n_{k+1}的下一個節點指向n_k。所以next\_node.next = n_k,也即n_k.next.next = n_k
要小心的是n_1的下一個必須指向 Ø 。如果你忽略了這一點,你的鏈表中可能會產生循環。如果使用大小爲 2 的鏈表測試代碼,則可能會捕獲此錯誤。

函數reverseList返回的是翻轉後的鏈表的頭節點,其中head.next=None,是必須的,因爲翻轉後,原頭節點的next是空。

class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        next_node = head.next
        p = self.reverseList(head.next)
        next_node.next = head
        head.next = None
        return p   

92. 反轉鏈表 II

https://leetcode-cn.com/problems/reverse-linked-list-ii/

反轉從位置 m 到 n 的鏈表。請使用一趟掃描完成反轉。

說明:1 ≤ m ≤ n ≤ 鏈表長度。

示例:輸入: 1->2->3->4->5->NULL, m = 2, n = 4,輸出: 1->4->3->2->5->NULL。

題解

一:借鑑206反轉鏈表的思路,其中reverse函數是翻轉m到n之間的鏈表,並返回翻轉之後的頭節點pre(4)以及原鏈表的下一個節點cur(5,即無需翻轉的第一個節點),主體結構的cur(2)是第一個要翻轉的節點(翻轉之後是新鏈表的尾節點),pre(1)是最後一個無需翻轉的節點。

class Solution(object):
    def reverseBetween(self, head, m, n):
        """
        :type head: ListNode
        :type m: int
        :type n: int
        :rtype: ListNode
        """
        def reverse(node):
            num = n - m + 1
            cnt = 1
            pre, cur = None, node
            while cnt <= num:
                next = cur.next
                cur.next = pre
                pre = cur
                cur = next
                cnt += 1
            return pre, cur

        before_head = ListNode(0)
        before_head.next = head
        pre, cur = before_head, head
        cnt = 1
        while cnt < m:
            pre = cur
            cur = cur.next     
            cnt += 1

        before, next = reverse(cur)
        pre.next, cur.next = before, next
        return before_head.next

83. 刪除排序鏈表中的重複元素

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/

給定一個排序鏈表,刪除所有重複的元素,使得每個元素只出現一次。

示例 1:輸入: 1->1->2,輸出: 1->2
示例 2:輸入: 1->1->2->3->3,輸出: 1->2->3

題解

一:下一個元素與當前重複的元素,讓該指針指向下一個元素的下一個(即跳掉下一個元素),若不重複,比較後續元素。因爲每個元素只出現一次,頭節點就依然會是頭節點,不會被刪除。與下一題的區別,最後一個節點的next肯定爲None,不要單獨處理。

class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        
        cur = head
        while cur.next:
            next = cur.next
            if cur.val == next.val:
                cur.next = next.next
            else:
                cur = cur.next
        return head

82. 刪除排序鏈表中的重複元素 II

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii/

給定一個排序鏈表,刪除所有含有重複數字的節點,只保留原始鏈表中 沒有重複出現 的數字。

示例 1:輸入: 1->2->3->3->4->4->5,輸出: 1->2->5
示例 2:輸入: 1->1->1->2->3,輸出: 2->3

題解

一:這邊與上一題的區別,頭節點可能會被刪掉,故添加一個虛擬節點,符合要求的鏈表的最後一個節點是pre,考慮當前節點,他若和前一個節點或者後一個節點相等,都要跳掉,若不相等,則將pre指向該節點,要注意每時每刻符合要求的最後一個節點都是pre,故遍歷完畢,要讓pre.next=None。

class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        before_head = ListNode(head.val - 1)
        before_head.next = head
        pre, cur, val = before_head, head, before_head.val

        while cur:
            next = cur.next
            if cur.val == val or (next and next.val == cur.val):
                val = cur.val
                cur = next
            else:
                pre.next = cur
                pre = cur
                cur = next
        pre.next = None
        return before_head.next

86. 分隔鏈表

https://leetcode-cn.com/problems/partition-list/

給定一個鏈表和一個特定值 x,對鏈表進行分隔,使得所有小於 x 的節點都在大於或等於 x 的節點之前。你應當保留兩個分區中每個節點的初始相對位置。

示例:輸入: head = 1->4->3->2->5->2, x = 3,輸出: 1->2->2->4->3->5

題解

一:創建兩個鏈表(小於是一個,大於等於是一個,在把這倆鏈表合併成一個),小trick:初始化爲兩個啞 ListNode,這樣可以避免判斷是否是第一個節點(小於、大於等於的),注意事項:要注意大於等於x的那個鏈表的尾節點的next要初始化爲空,不然他有可能會指向別的節點,從而形成環。

class Solution(object):
    def partition(self, head, x):
        """
        :type head: ListNode
        :type x: int
        :rtype: ListNode
        """
        less_head, great_head = ListNode(x), ListNode(x)
        cur, cur_less, cur_great = head, less_head, great_head
        while cur:
            next = cur.next
            if cur.val < x:
                cur_less.next = cur
                cur_less = cur
            else:
                cur_great.next = cur
                cur_great = cur
            cur = next
        cur_great.next = None
        cur_less.next = great_head.next
        return less_head.next

328. 奇偶鏈表

https://leetcode-cn.com/problems/odd-even-linked-list/

給定一個單鏈表,把所有的奇數節點和偶數節點分別排在一起。請注意,這裏的奇數節點和偶數節點指的是節點編號的奇偶性,而不是節點的值的奇偶性。請嘗試使用原地算法完成。你的算法的空間複雜度應爲 O(1),時間複雜度應爲 O(nodes),nodes 爲節點總數。

示例 1:輸入: 1->2->3->4->5->NULL,輸出: 1->3->5->2->4->NULL
示例 2:輸入: 2->1->3->5->6->4->7->NULL ,輸出: 2->3->6->7->1->5->4->NULL
說明:應當保持奇數節點和偶數節點的相對順序。鏈表的第一個節點視爲奇數節點,第二個節點視爲偶數節點,以此類推。

題解

一:還是兩個鏈表,第一個奇數節點,第二個偶數節點,做這類題目注意把握好循環進行下去的條件,即用那個指針進行判斷,可以用例子來看看是否有問題,例如[1,2,3,4]和[1,2,3,4,5]。

這邊要注意,循環體中的判斷第一處if even_cur.next是爲了確保even_cur.next.next這句話的正確性,第二處if odd_cur.next:是爲了確保odd_cur不爲None,保證odd_cur.next = even_head的正確性。

class Solution(object):
    def oddEvenList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        odd_head, even_head = head, head.next
        odd_cur, even_cur = odd_head, even_head

        while even_cur:
            odd_cur.next = even_cur.next
            if even_cur.next:
                even_cur.next = even_cur.next.next
            if odd_cur.next:
                odd_cur = odd_cur.next
            even_cur = even_cur.next
        odd_cur.next = even_head
        return odd_head

精簡一點,在循環的時候一起判斷,不再循環體中單獨判斷。

class Solution(object):
    def oddEvenList(self, head):
        if not head or not head.next:
            return head
        odd_head, even_head = head, head.next
        odd_cur, even_cur = odd_head, even_head

        while even_cur and even_cur.next:
            odd_cur.next = even_cur.next
            even_cur.next = even_cur.next.next
            odd_cur = odd_cur.next
            even_cur = even_cur.next
        odd_cur.next = even_head
        return odd_head

2. 兩數相加

https://leetcode-cn.com/problems/add-two-numbers/

給出兩個 非空 的鏈表用來表示兩個非負的整數。其中,它們各自的位數是按照 逆序 的方式存儲的,並且它們的每個節點只能存儲 一位 數字。如果,我們將這兩個數相加起來,則會返回一個新的鏈表來表示它們的和。您可以假設除了數字 0 之外,這兩個數都不會以 0 開頭。

示例:輸入:(2 -> 4 -> 3) + (5 -> 6 -> 4),輸出:7 -> 0 -> 8,原因:342 + 465 = 807

題解

一:沒啥注意的,依舊是關注指針是否爲None的問題(即我們是否用了None的next,也即合法性)。另外,所有的數加完之後,要判斷是否有進位,若有還要創建一個節點以對應該進位。

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        before_head = ListNode(0)
        cur, cur_1, cur_2, carry = before_head, l1, l2, 0

        while cur_1 or cur_2:
            val = carry
            if cur_1:
                val += cur_1.val
                cur_1 = cur_1.next
            if cur_2:
                val += cur_2.val
                cur_2 = cur_2.next
            carry = val // 10
            val = val % 10
            cur.next = ListNode(val)
            cur = cur.next
        if carry:
            cur.next = ListNode(carry)
        return before_head.next

445. 兩數相加 II

https://leetcode-cn.com/problems/add-two-numbers-ii/

給你兩個 非空 鏈表來代表兩個非負整數。數字最高位位於鏈表開始位置。它們的每個節點只存儲一位數字。將這兩數相加會返回一個新的鏈表。你可以假設除了數字 0 之外,這兩個數字都不會以零開頭。

進階:如果輸入鏈表不能修改該如何處理?換句話說,你不能對列表中的節點進行翻轉。

示例:輸入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4),輸出:7 -> 8 -> 0 -> 7

題解

一:反轉鏈表,再用題2的方法,得到結果之後繼續反轉

二:藉助數據結構,將鏈表中的數據存儲下來。這邊用的列表。

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        rec1, rec2, rec = [], [], []
        self._abstract_num(l1, rec1)
        self._abstract_num(l2, rec2)
        self._sum(rec1, rec2, rec)
        before_head = ListNode(0)
        cur = before_head
        for i in range(len(rec)-1, -1, -1):
            val = rec[i]
            cur.next = ListNode(val)
            cur = cur.next
        return before_head.next

    def _abstract_num(self, l, rec):
        cur = l
        while cur:
            rec.append(cur.val)
            cur = cur.next
    
    def _sum(self, rec1, rec2, rec):
        i, j, carry = len(rec1)-1, len(rec2)-1, 0
        while i >= 0 or j >= 0:
            val = carry
            if i >= 0:
                val += rec1[i]
                i -= 1
            if j >= 0:
                val += rec2[j]
                j -= 1
            carry = val // 10
            val = val % 10
            rec.append(val)
        if carry:
            rec.append(carry)

三:copy官方題解,https://leetcode-cn.com/problems/add-two-numbers-ii/,借用棧的結構後進先出,list的append和pop可做到,然後最後倒着把鏈表連起來

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        rec1, rec2 = [], []
        self._abstract_num(l1, rec1)
        self._abstract_num(l2, rec2)

        ans, carry = None, 0

        while rec1 or rec2 and carry != 0:
            x = rec1.pop() if rec1 else 0
            y = rec2.pop() if rec2 else 0
            val = carry + x + y
            carry = val // 10
            val = val % 10
            cur = ListNode(val)
            cur.next = ans
            ans = cur
        return ans
        
    def _abstract_num(self, l, rec):
        while l:
            rec.append(l.val)
            l = l.next

203. 移除鏈表元素

https://leetcode-cn.com/problems/remove-linked-list-elements/

刪除鏈表中等於給定值 val 的所有節點。

示例:輸入: 1->2->6->3->4->5->6, val = 6, 輸出: 1->2->3->4->5

題解

一:跳過就👌啦,要注意這句話pre.next = None,否則若最後一個元素應該被刪除,缺少這句話刪不掉,虛擬頭節點一直就是鏈表題目的小trick。

class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        before_head = ListNode(0)
        pre, cur = before_head, head

        while cur:
            if cur.val != val:
                pre.next = cur
                pre = cur
            cur = cur.next
        pre.next = None
        return before_head.next

21. 合併兩個有序鏈表

將兩個升序鏈表合併爲一個新的升序鏈表並返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。 

示例:輸入:1->2->4, 1->3->4,輸出:1->1->2->3->4->4

題解

一:不改變原先兩鏈表的結構

class Solution(object):
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        before_head = ListNode(0)
        pre = before_head

        while l1 or l2:
            if not l1:
                pre.next = ListNode(l2.val)
                l2 = l2.next
            elif not l2:
                pre.next = ListNode(l1.val)
                l1 = l1.next
            elif l1.val < l2.val:
                pre.next = ListNode(l1.val)
                l1 = l1.next
            else:
                pre.next = ListNode(l2.val)
                l2 = l2.next
            pre = pre.next
        return before_head.next

二:可以改變原先鏈表結構

class Solution(object):
    def mergeTwoLists(self, l1, l2):
        
        before_head = ListNode(0)
        pre = before_head

        while l1 and l2:
            if l1.val < l2.val:
                pre.next = l1
                l1 = l1.next
            else:
                pre.next = l2
                l2 = l2.next
            pre = pre.next
        pre.next = l1 if l1 else l2
        return before_head.next

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章