【Leetcode】鏈表——全題解

當前Leetcode的鏈表標簽題目一共53道題,除了會員題目,題解基本都在這了,還可能陸續更新一題多解~

簡單

(1)刪除節點

面試題 02.03. 刪除中間節點

237. 刪除鏈表中的節點

  • 如果當前節點有下一個節點,下一個節點也有下一個節點,那么把當前這個節點的值變為下一個節點的值,當前節點直接指向下一個節點的下一個節點(相當于刪除的不是當前節點,而是把當前節點變成它下一個節點,把它下一個節點刪除)
  • 如果當前節點有下一個節點,但是下一個節點沒有下一個節點了(當前節點是鏈表的倒數第二個節點),那么把當前節點的值變成下一個節點的值,當前節點指向None(還是相當于刪除了下一個節點)
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void Do not return anything, modify node in-place instead.
        """
        if node.next:
            if node.next.next:
                node.val = node.next.val
                node.next = node.next.next
            else:
                node.val = node.next.val
                node.next = None
        # 這一行有沒有都可以通過,因為當前節點不為最后一個節點,而且不要求返回
        return None
-------------------------------------------------------------------------------------------------
        # 也可以:
        if node.next:
            node.val = node.next.val
            if node.next.next:
                node.next = node.next.next
            else:
                node.next = None

面試題 02.01. 移除重復節點

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

  • 用字典保存節點的值,新建一個鏈表
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def removeDuplicateNodes(self, head: ListNode) -> ListNode:
        mydict = {}
        newhead = ListNode(0)
        n = newhead
        h = head

        while h:
            if h.val not in mydict:
                mydict[h.val] = True
                n.next = ListNode(h.val)
                n = n.next
            h = h.next
            
        return newhead.next

原地:

  • 新建節點作為新的頭節點,它的next為head
  • 使用兩個指針一個字典
  • 第一個指針指向新的頭節點,第二個節點往下遍歷,當遇到有新的值的節點,把第一個節點的next指向這個節點,把第一個節點指向這個節點,把這個節點的值加到字典中,第二個節點繼續往下遍歷
  • 如果第二個指針遇到當前節點的值已經在字典中了,繼續往下遍歷
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def removeDuplicateNodes(self, head: ListNode) -> ListNode:
        mydict = {}
        newhead = ListNode(0)
        newhead.next = head
        cur = newhead
        nex = cur.next
        
        while nex:
            if nex.val not in mydict:
                cur.next = nex
                mydict[nex.val] = True
                cur = cur.next
            nex = nex.next
        cur.next = None
        return newhead.next

203. 移除鏈表元素

  • 同樣的方法,題目稍有不同:
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        newhead = ListNode(0)
        newhead.next = head
        cur = newhead
        nex = cur.next

        while nex:
            if nex.val != val:
                cur.next = nex
                cur = cur.next
            nex = nex.next
        cur.next = None
        return newhead.next

劍指 Offer 18. 刪除鏈表的節點

  • 和上面一樣。。
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        newhead = ListNode(0)
        newhead.next = head
        cur, nex = newhead, newhead.next
        while nex:
            if nex.val != val:
                cur.next = nex
                cur = cur.next
            nex = nex.next
        cur.next = None
        return newhead.next

(2)反轉鏈表

206. 反轉鏈表

劍指 Offer 24. 反轉鏈表

image.png

把第一個節點指向newhead


image.png

newhead指向cur


image.png

cur指向nex
image.png

nex指向它的下一個
image.png
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if not head:
            return None
        newhead = None
        cur = head
        nex = cur.next
        while cur:
            cur.next = newhead
            newhead = cur
            cur = nex
            if nex:
                nex = nex.next
        return newhead

(3)雙指針:中間節點、環形鏈表

876. 鏈表的中間結點

  • 定義一個快指針一個慢指針,快指針每次走兩步,慢指針每次走一步,當快指針到鏈表的尾時,慢指針所在的位置就是鏈表的中間節點
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def middleNode(self, head: ListNode) -> ListNode:
        fast = head
        slow = head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        return slow

劍指 Offer 22. 鏈表中倒數第k個節點

  • 雙指針,一個快指針,一個慢指針
  • 快指針先走k步,走完k步之后,再和慢指針一起,每次走一步,當快指針到達鏈表的尾時,慢指針所在的位置就是要返回的倒數第k個節點
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        if not head:
            return None
        fast = head
        slow = head
        while k > 0:
            fast = fast.next
            k -= 1
        while fast:
            fast = fast.next
            slow = slow.next
        return slow

面試題 02.02. 返回倒數第 k 個節點

  • 與上一題稍有不同,返回的是節點的值
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def kthToLast(self, head: ListNode, k: int) -> int:
        if not head:
            return None
        fast, slow = head, head
        while k > 0:
            fast = fast.next
            k -= 1
        while fast:
            fast = fast.next
            slow = slow.next
        return slow.val

環形鏈表:

141. 環形鏈表

  • 一個快指針一個慢指針,快指針每次走兩步,慢指針每次走一步,如果快指針和慢指針走著走著相遇了,說明有環,返回True,否則返回False
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        slow = head
        fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                return True
        return False

兩個鏈表

21. 合并兩個有序鏈表

  • 如果兩個鏈表中有一個為空,直接返回另一個鏈表即可(如果兩個都為空,那么返回其中哪一個都是返回一個空鏈表)
  • 定義一個newhead,定義一個指針cur指向newhead
  • 兩個指針,一個指向l1的頭,一個指向l2的頭,比較當這兩個指針都不為空時,比較這兩個指針指向的節點的值,把較小的賦給cur,然后繼續遍歷
  • 當這兩個指針中有一個為空了,把不為空的那個指針指向的節點以及它以后的節點賦給cur
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        if not l1 or not l2:
            return l2 or l1
        tmp1, tmp2 = l1, l2
        newhead = ListNode(0)
        cur = newhead
        while tmp1 and tmp2:
            if tmp1.val > tmp2.val:
                cur.next = tmp2
                tmp2 = tmp2.next
            else:
                cur.next = tmp1
                tmp1 = tmp1.next
            cur = cur.next
        if tmp1:
            cur.next = tmp1
        else:
            cur.next = tmp2
        return newhead.next

(4)反轉鏈表和雙指針

234. 回文鏈表

面試題 02.06. 回文鏈表

  • 先用雙指針找到鏈表的中間節點,把從它的這個節點開始以后的節點組成的鏈表(也就是整個鏈表的后半部分)反轉
  • 雙指針:一個快指針一個慢指針,慢指針每次走一步,快指針每次走兩步,當快指針到達結尾的時候,慢指針所在的位置就是中間節點。要注意快指針的邊界,當快指針的不是None,而且快指針的下一個也不是None才可以繼續往下走。
  • 比較前半部分和反轉后的后半部分,這兩個鏈表中只要有一個鏈表遍歷到結尾就可以結束遍歷,因為不管這個鏈表的節點是奇數個還是偶數個,都可以是一個回文鏈表
    也就是例如 1——>2——>3——>2——>1,分成兩個鏈表分別是1——>2和1——>2——>3比較,只要1,2相同即可。
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        if not head:
            return True
        
        slow = head
        fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        
        newhead = None
        cur = slow
        nex = cur.next
        while cur:
            cur.next = newhead
            newhead = cur
            cur = nex
            if nex:
                nex = nex.next
        
        h = head
        while h and newhead:
            if h.val == newhead.val:
                h = h.next
                newhead = newhead.next
            else:
                return False
        return True

(5)雙指針解決兩個鏈表相交問題

160. 相交鏈表

劍指 Offer 52. 兩個鏈表的第一個公共節點

面試題 02.07. 鏈表相交

  • 兩個指針,一個指針A從鏈表A的頭開始走,另外一個指針B,從鏈表B的頭開始走,當A或B走完自己的鏈表時,繼續走對方的鏈表,如果指針A和指針B相遇了,返回指針A(這時候的返回有兩種情況,一是有環的情況,AB相遇的位置是兩個鏈表的第一個公共節點,二是沒有環的情況,這時候返回的是None,因為AB都同時走了一樣的路程:鏈表A和B,到達了終點相遇)
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        if not headA or not headB:
            return None
        tmpA, tmpB = headA, headB
        while tmpA != tmpB:
            if tmpA:
                tmpA = tmpA.next
            else:
                tmpA = headB
            if tmpB:
                tmpB = tmpB.next
            else:
                tmpB = headA
        return tmpA

(6)其他

劍指 Offer 06. 從尾到頭打印鏈表

  • 遍歷,把節點的值存在列表中,返回列表的逆序即可
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reversePrint(self, head: ListNode) -> List[int]:
        res = []
        h = head
        while h:
            res.append(h.val)
            h = h.next
        return res[::-1]

1290. 二進制鏈表轉整數

舉例:
如果輸入是[1,0,0,1,0],它的十進制樹應該是18.


image.png

那么二進制轉成十進制是這么算的:


image.png

定義一個res用來返回結果,每當遍歷到一個新的節點,就把前面res的值*2再加上當前節點的值:
image.png

這樣第一個1一共乘了四個2,第二個1一共乘了一個2,加在一起正好是返回的結果。
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getDecimalValue(self, head: ListNode) -> int:
        res = 0
        cur = head
        while cur:
            res = res * 2 + cur.val
            cur = cur.next
        return res

中等

(1)刪除節點

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

  • 時間復雜度是O(n),空間復雜度小于O(n)
  • 用一個字典來保存每一個節點的值出現了多少次
  • 利用雙指針刪除節點
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        mydict = {}
        cur = head
        newhead = ListNode(0)
        newhead.next = head
        while cur:
            if cur.val in mydict:
                mydict[cur.val] += 1
            else:
                mydict[cur.val] = 1
            cur = cur.next
        cur = newhead
        nex = cur.next
        while nex:
            if mydict[nex.val] == 1:
                cur.next = nex
                cur = cur.next
            nex = nex.next
        cur.next = None
        return newhead.next

因為是排序鏈表,還可以使用三指針:
參考:
https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii/solution/chao-qing-xi-tu-jie-san-zhi-zhen-fa-by-justdo1t/

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        newhead = ListNode(0)
        newhead.next = head
        cur, left, right = newhead, newhead.next, newhead.next
        while right:
            while right.next and right.next.val == right.val:
                right = right.next
            if right == left:
                cur.next = left
                cur = cur.next
            left = right.next
            right = left
        cur.next = None
        return newhead.next
        

19. 刪除鏈表的倒數第N個節點

  • 雙指針
  • 因為被刪除的節點至少是“倒數第一個”節點,所以如果鏈表本身就為空,那就直接返回空,鏈表本身只有一個節點,一定會刪除倒數第一個節點,所以也返回空
  • 快指針先走n個節點,如果這時fast為空,說明fast正好走完了鏈表,那么n既鏈表節點數,也就是長度為n的鏈表,刪除倒數第n個節點,也就是刪除第一個節點,這時直接返回head.next
  • 如果fast先走n步,走完fast不為空,那么fast和slow一起繼續走,并且循環while的條件是fast與fast.next都不為空,也就是fast最遠走到最后一個節點
  • 因為fast比slow走得快,所以可以保證slow.next一定存在,當fast走到最后一個節點,此時slow所在的位置就是被刪除節點的前一個節點,此時只要將slow.next指向slow.next.next,因為slow.next一定存在(如上述),所以slow.next指向slow.next.next不會報錯
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        if not head or not head.next:
            return None
        slow, fast = head, head
        while n > 0:
            fast = fast.next
            n -= 1
        if not fast:
            return head.next
        while fast and fast.next:
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next
        return head

(2)變換鏈表

24. 兩兩交換鏈表中的節點

  • 三個指針
  • 新建個頭節點newhead,newhead的next指向head
  • 用四個指針,cur,cur1,cur2,nex
  • cur初始指向newhead,cur1指向head的第一個節點,cur2指向head的第二個節點,nex指向第三個
  • 把cur的next指向cur2,cur2的next指向cur1,cur1的next指向None,就完成了兩個節點的翻轉
  • 然后把cur1指向nex,cur2指向cur1的next,nex指向cur2的next
  • 注意退出循環的條件
  • 退出循環后,如果cur1還指向某個節點,要把這個節點加到鏈表的尾部
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        
        cur1, cur2 = head, head.next
        nex = cur2.next
        newhead = ListNode(0)
        newhead.next = head
        cur = newhead
        while cur1 and cur2:
            cur.next = cur2
            cur2.next = cur1
            cur = cur1
            cur.next = None
            cur1 = nex
            if cur1 and cur1.next:
                cur2 = cur1.next
            else:
                break
            nex = cur2.next
        if cur1:
            cur.next = cur1
        return newhead.next

61. 旋轉鏈表

  • 先遍歷一遍鏈表計算鏈表的長度
  • 把k和長度取余,然后使用快慢指針找到要旋轉的節點,拼接一下即可
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def rotateRight(self, head: ListNode, k: int) -> ListNode:
        if k == 0 or not head:
            return head
        lenth = 0
        h = head
        while h:
            lenth += 1
            h = h.next
        lenth = k % lenth
        if lenth == 0:
            return head
        fast = head
        slow = head
        while lenth > 0:
            fast = fast.next
            lenth -= 1
        pivot = fast
        while fast and fast.next:
            fast = fast.next
            slow = slow.next
            pivot = pivot.next
        newhead = slow.next
        slow.next = None
        pivot.next = head
        return newhead

148. 排序鏈表

  • 歸并,把每個節點都分成一個一個節點
  • 再把兩個兩個排好序拼在一起
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        fast, slow = head.next, head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        right_head = slow.next
        slow.next = None
        left, right = self.sortList(head), self.sortList(right_head)
        return self.merge(left, right)

    # 合并兩個鏈表
    def merge(self, head1, head2):
        h1, h2 = head1, head2
        newhead = ListNode(0)
        cur = newhead
        while h1 and h2:
            if h1.val > h2.val:
                cur.next = h2   
                h2 = h2.next
            else:
                cur.next = h1
                h1 = h1.next
            cur = cur.next
        cur.next = h1 or h2
        return newhead.next

86. 分隔鏈表

面試題 02.04. 分割鏈表

  • 新建兩個頭節點,一個用來保存比x小的節點,另一個用來保存其他的節點
  • 最后把這兩個鏈表合并即可
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def partition(self, head: ListNode, x: int) -> ListNode:
        before, after = ListNode(0), ListNode(0)
        cur1, cur2 = before, after
        h = head
        while h:
            if h.val < x:
                cur1.next = h
                cur1 = cur1.next
            else:
                cur2.next = h
                cur2 = cur2.next
            h = h.next
        cur2.next = None
        cur1.next = after.next
        return before.next

92. 反轉鏈表 II

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
        newhead = ListNode(0)
        newhead.next = head
        pre = newhead
        for _ in range(m - 1):
            pre = pre.next
        tmp_head = None
        cur = pre.next
        for _ in range(n - m + 1):
            nex = cur.next
            cur.next = tmp_head
            tmp_head = cur
            cur = nex
        pre.next.next = cur
        pre.next = tmp_head
        return newhead.next

109. 有序鏈表轉換二叉搜索樹

  • 遍歷鏈表找到中點,中點的值作為一個根,這個節點的左節點是左半邊鏈表的中點,右節點是這個點右半邊鏈表的中點
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def sortedListToBST(self, head: ListNode) -> TreeNode:
        if not head:
            return None
        if not head.next:
            return TreeNode(head.val)
        fast, slow = head.next.next, head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        root = TreeNode(slow.next.val)
        root.right = self.sortedListToBST(slow.next.next)
        slow.next = None
        root.left = self.sortedListToBST(head)
        return root

328. 奇偶鏈表

  • 把head作為奇鏈表的頭和尾
  • 把head.next作為偶鏈表的頭和尾
  • 遍歷鏈表,當奇偶鏈表的尾還有下一個元素,就繼續遍歷
  • 初始情況如下


    image.png
  • 奇鏈表的尾的下一個節點總是偶節點,偶鏈表的尾的下一個節點總是奇節點,所以把奇鏈表的尾指向偶鏈表的尾的下一個節點,把奇鏈表的尾移到這個節點,再把偶鏈表的尾指向奇鏈表的尾的下一個節點,把偶鏈表的尾移到這個節點


    image.png

    image.png

    image.png

    image.png
  • 當奇鏈表的尾的下一個和偶鏈表的尾的下一個都為空時,把奇鏈表的尾的下一個指向偶鏈表的頭即可


    image.png
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def oddEvenList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        odd_head = head
        odd_tail = head
        even_head = head.next
        even_tail = head.next
        while odd_tail.next and even_tail.next:
            odd_tail.next = even_tail.next
            odd_tail = odd_tail.next
            even_tail.next = odd_tail.next
            even_tail = even_tail.next
        odd_tail.next = even_head
        return odd_head

143. 重排鏈表

  • 找到鏈表的中間節點,把后半部分反轉
  • 穿插合并前半條鏈表和后半條鏈表
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reorderList(self, head: ListNode) -> None:
        """
        Do not return anything, modify head in-place instead.
        """
        if not head:
            return head
        # 找到鏈表的中間節點
        fast, slow = head, head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        # 反轉后半部分鏈表
        cur = slow.next
        slow.next = None
        newhead = None
        while cur:
            nex = cur.next
            cur.next = newhead
            newhead = cur
            cur = nex
        
        cur1 = head
        cur2 = newhead
        while cur1 and cur2:
            nex1 = cur1.next
            nex2 = cur2.next
            cur1.next = cur2
            cur1 = nex1
            cur2.next = cur1
            cur2 = nex2
        return head

725. 分隔鏈表

  • 先計算一下鏈表長度,如果鏈表長度大于等于要分割的塊數,如用鏈表長度除以要分割多少塊,求得每個子鏈表都需要有多少個節點partlen。再計算如果每塊都有partlen個節點,最后還剩多少節點extra,多出來的這些節點要加到每個子鏈表上,每個子鏈表加一個節點,加到extra剩余為0為止。
  • 然后開始遍歷分割鏈表,把每個子鏈表的頭存到res結果集中,要把每個子鏈表的尾切割開
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def splitListToParts(self, root: ListNode, k: int) -> List[ListNode]:
        res = []
        lenth, extra, part_len = 0, 0, 0

        h = root
        while h:
            lenth += 1
            h = h.next

        if lenth >= k:
            extra = lenth % k
            part_len = lenth // k
        else:
            part_len = 1

        cur, nex = root, root
        while k > 0:
            tmp_lenth = 0
            k -= 1
            tmp_lenth = part_len + (extra > 0)
            extra = max(0, extra - 1)
            res.append(nex)
            while cur and tmp_lenth > 1:
                tmp_lenth -= 1
                cur = cur.next 
            if cur:
                nex = cur.next
                cur.next = None
                cur = nex          
        return res

(3)環路檢測

面試題 02.08. 環路檢測

142. 環形鏈表 II

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        fast, slow = head, head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                fast = head
                while slow != fast:
                    fast = fast.next
                    slow = slow.next
                return slow            
        return None

(4)求值

2. 兩數相加

面試題 02.05. 鏈表求和

  • 核心代碼只有 while tmp1 and tmp2這一塊,就是不斷求和,注意進位
  • 如果兩個鏈表中有一個遍歷完了,注意要把剩的那個列表繼續遍歷完
  • 如果兩個鏈表都遍歷完了,還要注意最后是否還有進位
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        res, flag = 0, 0
        tmp1, tmp2 = l1, l2
        newhead = ListNode(0)
        cur = newhead
        while tmp1 and tmp2:
            cur.next = ListNode((tmp1.val + tmp2.val + flag) % 10)
            flag = (tmp1.val + tmp2.val + flag) // 10
            cur = cur.next
            tmp1, tmp2 = tmp1.next, tmp2.next
        while tmp1:
            cur.next = ListNode((tmp1.val + flag) % 10)
            flag = (tmp1.val + flag) // 10
            cur = cur.next
            tmp1 = tmp1.next
        while tmp2:
            cur.next = ListNode((tmp2.val + flag) % 10)
            flag = (tmp2.val + flag) // 10
            cur = cur.next
            tmp2 = tmp2.next
        if flag > 0:
            cur.next = ListNode(flag)
        return newhead.next

445. 兩數相加 II

  • 可以把兩個鏈表翻轉,然后按照兩數相加一的算法來做這道題目
  • 使用棧(官方題解):
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        stack1, stack2 = [], []
        cur = l1
        while cur:
            stack1.append(cur.val)
            cur = cur.next
        cur = l2
        while cur:
            stack2.append(cur.val)
            cur = cur.next
        flag = 0
        newhead = None
        while stack1 or stack2 or flag != 0 :
            cur1, cur2 = 0, 0
            if stack1:
                cur1 = stack1.pop()
            if stack2:
                cur2 = stack2.pop()
            tmp = cur1 + cur2 + flag
            flag = tmp // 10
            tmp %= 10
            cur = ListNode(tmp)
            cur.next = newhead
            newhead = cur
        return newhead

817. 鏈表組件

  • 遍歷列表把每個值放在字典中
  • 遍歷鏈表,如果當前的值存在在字典中,flag為0,說明當前節點是組件的第一個節點,res+1,如果當前值存在在字典中,flag為1,繼續遍歷,如果當前值不在字典中,繼續遍歷,把flag置為0
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def numComponents(self, head: ListNode, G: List[int]) -> int:
        mydict = {}
        for i in G:
            if i not in mydict:
                mydict[i] = True
        cur = head
        res = 0
        flag = 0
        while cur:
            if cur.val in mydict and flag == 0:
                res += 1
                flag = 1
            elif cur.val not in mydict:
                flag = 0
            cur = cur.next
        return res

其他

1019. 鏈表中的下一個更大節點

  • 用一個棧來保存當前未找到更大節點值的節點
  • res用來保存返回的結果
  • 如果遍歷到當前節點的值比棧頂的節點值還小或相等,把當前這個節點和它的下標保存到棧中
  • 如果遍歷到當前節點的值比棧頂的節點值大,就while循環,把當前節點的值都保存到棧頂下標所在的res中去,然后把當前節點append到棧中
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def nextLargerNodes(self, head: ListNode) -> List[int]:
        res, stack = [], []
        index = 0
        cur = head
        while cur:
            res.append(0)
            while stack and cur.val > stack[-1][0].val:
                node = stack.pop()
                res[node[1]] = cur.val
            stack.append([cur,index])
            cur = cur.next
            index += 1
        return res

1367. 二叉樹中的列表

  • 官方題解:遞歸
  • 兩個函數,一個用來向下遞歸找到head的值與節點值相等的節點
  • 另一個函數用來找當前的相等節點的下一個節點和head.next的值是否相等
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isSubPath(self, head: ListNode, root: TreeNode) -> bool:
        if not root:
            return False
        return self.helper(head, root) or self.isSubPath(head, root.left) or self.isSubPath(head, root.right)

    def helper(self, head, root):
        if not head:
            return True
        if not root:
            return False
        if head.val != root.val:
            return False
        return self.helper(head.next, root.left) or self.helper(head.next, root.right)

1171. 從鏈表中刪去總和值為零的連續節點

  • 定義一個字典,遍歷兩次鏈表
  • 遍歷的時候維護一個sum
  • 第一次遍歷的時候,sum每遇到一個節點就加上這個節點的值,然后把sum作為key,當前的節點作為value保存在字典中,這樣相同sum的結點,在前面出現的就會被后面出現的覆蓋掉
  • 第二次遍歷的時候,依然維護這個sum,當前節點的下一個節點就是字典中sum對應的節點的下一個節點
    第一次遍歷:


    image.png

    第二次遍歷:


    image.png

    image.png

    image.png

    image.png

    image.png
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def removeZeroSumSublists(self, head: ListNode) -> ListNode:
        mydict = {}
        mysum = 0
        newhead = ListNode(0)
        cur = newhead
        newhead.next = head
        while cur:
            mysum += cur.val
            mydict[mysum] = cur
            cur = cur.next
        
        mysum = 0
        cur = newhead
        while cur:
            mysum += cur.val
            cur.next = mydict[mysum].next
            cur = cur.next
        return newhead.next

138. 復制帶隨機指針的鏈表

劍指 Offer 35. 復雜鏈表的復制

  • 使用字典+遍歷
  • 第一次遍歷:使用字典node_to_index,遍歷鏈表,num由0開始,目的是把原來鏈表的每個節點作為key,num作為value保存在字典中。
    • 例如[[7,null],[13,0],[11,4],[10,2],[1,0]]
    • 字典中存儲的是:{[節點“7”:0],[節點“13”:1],[節點“11”:2],[節點“10”:3],[節點“1”:4]}


      image.png
  • 第二次遍歷:這次每個節點的index我們都知道了,第二次用字典index_to_random_index,把每個節點的index作為key,把這個節點的random的index作為value,保存在index_to_random_index字典中,random節點可以訪問到,就可以用這個節點在第一次遍歷得到的字典中取到這個random節點的index


    image.png
  • 第三次遍歷:同時遍歷原來的鏈表和新建一個新的頭節點,用原來鏈表的值建一個新的鏈表。
  • 第四次遍歷:新建一個字典index_to_newnode,把節點的下標作為key,節點作為value保存到數組中。


    image.png
  • 第五次遍歷:把新鏈表的每個節點的random添加進去,因為知道當前節點的下標,知道每個下標對應的random的下標,也知道每個下標對應的新鏈表的節點


    image.png

    image.png

    image.png

    image.png

    image.png
  • 代碼實現的時候,有些循環合并了,所以只循環了三次:
"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        if not head:
            return None
        newhead = Node(0)
        cur = newhead
        tmp = head

        num = 0
        mydict_node_to_index = {}
        index_to_mynewnode = {}
        while tmp:
            cur.next = Node(tmp.val)
            cur = cur.next
            mydict_node_to_index[tmp] = num
            index_to_mynewnode[num] = cur
            num += 1
            tmp = tmp.next

        index_to_random_index = {}
        tmp = head
        while tmp:
            index_to_random_index[mydict_node_to_index[tmp]] = None if not tmp.random else mydict_node_to_index[tmp.random]
            tmp = tmp.next
        
        cur = newhead.next
        num = 0
        while cur:
            if index_to_random_index[num] is not None:
                cur.random = index_to_mynewnode[index_to_random_index[num]]
            cur = cur.next
            num += 1

        return newhead.next

一樣的寫法:

"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""
class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        if not head:
            return None

        newhead = Node(0)
        cur = newhead
        tmp = head
        node_to_index = {}
        index_to_newhead = {}
        num = 0
        while tmp:
            cur.next = Node(tmp.val)
            cur = cur.next
            index_to_newhead[num] = cur
            node_to_index[tmp] = num
            num += 1
            tmp = tmp.next
        
        index_to_random_index = {}
        num = 0
        tmp = head
        while tmp:
            index_to_random_index[num] = node_to_index[tmp.random] if tmp.random is not None else None
            tmp = tmp.next
            num += 1
        
        cur = newhead.next
        num = 0
        while cur:
            if index_to_random_index[num] is not None:
                cur.random = index_to_newhead[index_to_random_index[num]]
            cur = cur.next
            num += 1
        return newhead.next

430. 扁平化多級雙向鏈表

具體實現看代碼吧。
注意1是要把原來的child置為空,2是prev要有指向。
要是寫完發現節點的順序是對的,但是不是一個有效的雙向鏈表估計就是注意1和2的問題,用循環打印一下當前節點的值和當前節點的prev值看看是不是有沒連上的。

"""
# Definition for a Node.
class Node:
    def __init__(self, val, prev, next, child):
        self.val = val
        self.prev = prev
        self.next = next
        self.child = child
"""

class Solution:
    def flatten(self, head: 'Node') -> 'Node':
        if not head:
            return head
        cur = head
        while cur:
            if cur.child:
                self.helper(cur)
            cur = cur.next
        return head
    
    def helper(self, node):
        right = node.next
        node.next = node.child
        node.child.prev = node
        node.child = None
        while node.next:
            if node.child:
                self.helper(node)
            node = node.next
        node.next = right
        if right:
            right.prev = node
        return node

147. 對鏈表進行插入排序

  • 新建頭節點
  • 維護一個tail,一開始指向新的頭節點
  • 遍歷鏈表,如果當前的節點比tail節點的值大,直接插在tail節點后,把tial指向這個新插入的節點,記得tail的next要指向空,要不就無限循環了。。
  • 如果當前節點值比tail小,那么新建一個tmp指向頭,找到當前節點應該插入的為止,把它插進去
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def insertionSortList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        newhead = ListNode(float('-inf'))
        tail = newhead
        cur = head
        nex = cur
        while cur:
            nex = nex.next
            if cur.val < tail.val:
                tmp = newhead
                while cur.val > tmp.val and cur.val > tmp.next.val:
                    tmp = tmp.next
                cur.next = tmp.next
                tmp.next = cur
            else:
                tail.next = cur
                tail = tail.next
                tail.next = None
            cur = nex
        return newhead.next

困難

23. 合并K個排序鏈表

解法一:

  • 用小根堆把所有節點的值都保存起來,然后用這些值重新建一個新鏈表
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        heap = []
        for i in lists:
            while i:
                heapq.heappush(heap,i.val)
                i = i.next
        head = ListNode(0)
        cur = head
        while heap:
            cur.next = ListNode(heap[0])
            heapq.heappop(heap)
            cur = cur.next
        return head.next

其他解法待更新~

25. K 個一組翻轉鏈表

  • 用pre來標志之前已經翻轉過的鏈表尾
  • cur表示當前要遍歷的節點
  • 定義一個newhead做新的鏈表頭節點,把head賦給newhead的next
  • 然后開始遍歷,每到一個新的cur,把tail指向這個cur,然后tail向下遍歷,找到要翻轉的子鏈表的尾
  • 使用helper函數,把cur和tail傳給這個函數
  • helper函數的實現:
    • 把tail的next作為pre保存起來
    • 當tail和pre不相同時,遍歷繼續
    • 遍歷的內容是從傳遞過來的cur開始把cur傳給tmp,把tmp的next作為nex保存起來,再把tmp的next指向pre,這樣就完成了一個節點的翻轉,然后把pre指向當前節點,tmp指向nex,nex指向tmp.next
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        newhead = ListNode(0)
        newhead.next = head
        pre = newhead
        cur = head
        while cur: 
            tail = cur
            for i in range(1, k):
                tail = tail.next
                if not tail:
                    return newhead.next
            nex = tail.next
            cur, tail = self.helper(cur, tail)
            pre.next = cur
            tail.next = nex
            pre = tail
            cur = tail.next
        return newhead.next

    def helper(self, head, tail):
        pre = tail.next
        tmp = head
        while pre != tail:
            nex = tmp.next
            tmp.next = pre
            pre = tmp
            tmp = nex
        return tail, head
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容