leetcode(12)链表系列题目


21. 合并两个有序链表

class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        if not list1:
            return list2
        if not list2:
            return list1
        if list1.val < list2.val:
            list1.next = self.mergeTwoLists(list1.next, list2)
            return list1
        else:
            list2.next = self.mergeTwoLists(list1, list2.next)
            return list2

23. 合并K个升序链表

思路 1:
归并,分而治之
链表两两合并

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        if not lists:
            return 
        n = len(lists)
        return self.merge(lists, 0, n - 1)
    def merge(self, lists, left, right):
        if left == right:
            return lists[left]
        mid = (left + right) // 2
        l = self.merge(lists, left, mid)
        r = self.merge(lists, mid + 1, right)
        return self.mergeTwo(l, r)
    def mergeTwo(self, l1, l2):
        if not l1:
            return l2
        if not l2:
            return l1
        if l1.val < l2.val:
            l1.next = self.mergeTwo(l1.next, l2)
            return l1
        else:
            l2.next = self.mergeTwo(l1, l2.next)
            return l2

思路 2:
调用优先级队列,实现最小堆
时间复杂度:O(n?log(k)),n 是所有链表中元素的总和,k 是链表个数。

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        import heapq  #调用堆
        minHeap = []
        for l in lists:
            while l:
                heapq.heappush(minHeap, l.val)  #把l中的数据逐个加到堆中
                l = l.next
        dummy = ListNode(0)
        p = dummy  #构造虚节点
        while minHeap:
            val = heapq.heappop(minHeap) #依次弹出最小堆的数据
            p.next = ListNode(val)
            p = p.next
        return dummy.next

206.反转链表

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        pre, cur = None, head
        while cur:
            tmp = cur.next
            cur.next = pre
            pre = cur
            cur = tmp
        return pre

25. K 个一组翻转链表

注意:与 206.反转链表 的区别是需要判断节点是否够K个一组,并且要递归的调用翻转

class Solution:
    def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        cur = head
        for _ in range(k):
            if not cur:
                return head  # 如果head后续节点数不足k个直接返回head
            cur = cur.next
        
        pre, cur = None, head
        for _ in range(k):  # 将包括head往后的k个节点翻转
            tmp = cur.next
            cur.next = pre
            pre = cur
            cur = tmp
        
        head.next = self.reverseKGroup(cur, k)  # 递归得到下一段k个节点的反转后的头结点
        # head已经是当前段k个节点的尾节点了,指向下一段的反转后的头结点
        return pre

24. 两两交换链表中的节点

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        res = ListNode(next = head)
        pre = res
        while pre.next and pre.next.next:
            cur = pre.next
            tmp = pre.next.next

            cur.next = tmp.next
            tmp.next = cur
            pre.next = tmp
            pre = pre.next.next
        return res.next

92. 反转链表 II

class Solution:
    def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
        # 设置 dummyNode 是这一类问题的一般做法
        # dummy = ListNode(-1)
        # dummy.next = head
        dummy = ListNode(next = head)  # 等价于
        pre = dummy
        for _ in range(left - 1):
            pre = pre.next

        cur = pre.next
        for _ in range(right - left):
            next = cur.next
            cur.next = next.next
            next.next = pre.next
            pre.next = next

        return dummy.next

876. 链表的中间结点

双指针

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

143. 重排链表

简洁代码,取中,翻转,合并

class Solution:
    def reorderList(self, head: ListNode) -> None:
        if not head:
            return
        # 找到中点
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        # 翻转后半
        pre, cur = None, slow.next
        slow.next = None
        while cur:
            tmp = cur.next
            cur.next = pre
            pre = cur
            cur = tmp
        # 交替合并
        left, right = head, pre
        while right:
            tmp = left.next  #注意这里是left.next
            left.next = right
            left = right
            right =tmp

19. 删除链表的倒数第 N 个结点

141. 环形链表

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

142. 环形链表 II


相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。

因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:

(x + y) * 2 = x + y + n (y + z)

两边消掉一个(x+y): x + y = n (y + z)

因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。

所以要求x ,将x单独放在左面:x = n (y + z) - y ,

再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。
当 n为1的时候,公式就化解为 x = z,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。
即从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

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

从递归到迭代:由浅入深……

相关