leetcode(12)链表系列题目

707. 设计链表

单链表

class Node:
    def __init__(self, val):
        self.val = val
        self.next = None

class MyLinkedList:

    def __init__(self):
        self.head = Node(0)  # 虚拟头部节点
        self.size = 0  # 添加的节点数

    def get(self, index: int) -> int:
        if 0 <= index < self.size:
            dummy = self.head
            for _ in range(index + 1):
                dummy = dummy.next
            return dummy.val
        else:
            return -1

    def addAtHead(self, val: int) -> None:
        self.addAtIndex(0, val)

    def addAtTail(self, val: int) -> None:
        self.addAtIndex(self.size, val)

    def addAtIndex(self, index: int, val: int) -> None:
        if index < 0:
            index = 0
        elif index > self.size:
            return
        dummy = self.head
        for _ in range(index):
            dummy = dummy.next
        node = Node(val)
        node.next = dummy.next  # 先把待插入位置后面的部分接到node后面
        dummy.next = node   # 再把node接到dummy后面
        self.size += 1

    def deleteAtIndex(self, index: int) -> None:
        if 0 <= index < self.size:
            dummy = self.head
            for _ in range(index):
                dummy = dummy.next
            dummy.next = dummy.next.next
            self.size -= 1

查找链表

160. 相交链表

同一题面试题 02.07. 链表相交
有的链表短,他走完了就去走另一条链表。如果有交点,他们最终一定会在同一个位置相遇

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        l1, l2 = headA, headB
        while l1 != l2:
            l1 = l1.next if l1 else headB
            l2 = l2.next if l2 else headA
        return l1

剑指 Offer 22. 链表中倒数第k个节点

注意:使用快慢指针

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        fast, slow = head, head
        for _ in range(k):
            fast = fast.next
        while fast:
            fast = fast.next
            slow = slow.next
        return slow

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

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

反转链表

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

92. 反转链表 II

注意:设置 dummyNode

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(left, right):
        # for _ in range(right - left):  # 等价于
            tmp = cur.next
            cur.next = tmp.next
            tmp.next = pre.next
            pre.next = tmp
        return dummy.next

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

通过移动pre,cur 和 tmp 都在循环里面赋值

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        dummy = ListNode(next = head)
        pre = dummy
        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 dummy.next

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

61. 旋转链表

步骤:

  1. 求链表长度;
  2. 找出倒数第 k+1 个节点;
  3. 链表重整:将链表的倒数第 k+1 个节点和倒数第 k 个节点断开,并把后半部分拼接到链表的头部。
class Solution:
    def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        if not head or not head.next: return head
        n = 0
        cur = head
        while cur:  # 步骤1
            n += 1
            cur = cur.next
        k %= n  # 注意要先取余之后再旋转
        if k == 0: return head
        fast, slow = head, head
        while k:  # 步骤2
            fast = fast.next
            k -= 1
        while fast.next:  # 注意是fast.next
            fast = fast.next
            slow = slow.next
        newHead = slow.next  # 步骤3
        slow.next = None
        fast.next = head
        return newHead

86. 分隔链表

用两个dummy记录两个链表,一个链表放小于x的节点,一个链表放大于等于x的节点
最后,拼接这两个链表.

class Solution:
    def partition(self, head: ListNode, x: int) -> ListNode:
        p1 = dummy1 = ListNode(0)
        p2 = dummy2 = ListNode(0)
        # dummy1, dummy2 = ListNode(0), ListNode(0)
        # p1 ,p2 = dummy1, dummy2  # 等价于
        while head:
            if head.val < x:
                p1.next = head
                p1 = p1.next
            else:
                p2.next = head
                p2 = p2.next
            head = head.next
        p1.next = dummy2.next  # 注意是dummy2.next而不是dummy2
        p2.next = None
        return dummy1.next

138. 复制带随机指针的链表

同一题剑指 Offer 35. 复杂链表的复制

  1. 复制各节点到原始节点的后面,并拼接
  2. 构建各新节点的 random 指向
  3. 拆分两链表
class Solution:
    def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
        if not head:
            return head
        cur = head
        while cur:  # 步骤1
            tmp = Node(cur.val)
            tmp.next = cur.next
            cur.next = tmp
            cur = tmp.next
        
        cur = head
        while cur:  # 步骤2
            if cur.random:
                cur.next.random = cur.random.next
            cur = cur.next.next

        pre, cur = head, head.next
        res = cur  # 用一个链表接住结果
        while cur.next:  # 步骤3,注意是cur.next
            pre.next = pre.next.next
            pre = pre.next
            cur.next = cur.next.next
            cur = cur.next
        pre.next = None  # 单独处理原链表尾节点
        return res  # 返回新链表头节点

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

147. 对链表进行插入排序

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def insertionSortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head: return head
        dummy = ListNode(0)
        dummy.next = head
        tmp = head
        cur = head.next

        while cur:
            if tmp.val <= cur.val:
                tmp = tmp.next
            else:
                pre = dummy
                while pre.next.val <= cur.val:
                    pre = pre.next               
                tmp.next = cur.next  # 先把后面连接好,再连前面的
                cur.next = pre.next
                pre.next = cur
            cur = tmp.next
        return dummy.next         

删除链表元素

203. 移除链表元素

注意:设置 dummyNode,cur = dummy,判断cur.next

class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        dummy = ListNode(next = head)
        cur = dummy
        while cur.next:
            if cur.next.val == val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return dummy.next

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

注意:设置 dummyNode,fast, slow = head, dummy,快慢指针,先找到倒数第n个数

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummy = ListNode(next = head)
        fast, slow = head, dummy
        for _ in range(n):
            fast = fast.next
        while fast:
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next
        return dummy.next

83. 删除排序链表中的重复元素

注意:不重复的时候继续往后遍历

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head:
            return head
        cur = head
        while cur.next: # 需要后面一个不为空才能进行判断
            if cur.val == cur.next.val:
                cur.next = cur.next.next
            else:
                cur = cur.next # 继续往后遍历
        return head

82. 删除排序链表中的重复元素 II

注意:设置 dummyNode
如果 cur.next 与 cur.next.next 对应的元素相同,就将 cur.next 以及所有后面拥有相同元素值的链表节点全部删除。
记下这个元素值 x,随后不断将 cur.next 从链表中移除,直到 cur.next 为空节点或者其元素值不等于 x 为止。此时,我们将链表中所有元素值为 x 的节点全部删除

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head:
            return head
        dummy = ListNode(next = head)
        cur = dummy
        while cur.next and cur.next.next:
            if cur.next.val == cur.next.next.val:
                x = cur.next.val
                while cur.next and cur.next.val == x:
                    cur.next = cur.next.next
            else:
                cur = cur.next
        return dummy.next

合并链表

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

剑指 Offer 18. 删除链表的节点

剑指 Offer 06. 从尾到头打印链表

剑指 Offer 35. 复杂链表的复制

参考资料:
从递归到迭代:由浅入深……

posted @ 2022-05-15 21:10  YTT77  阅读(42)  评论(0编辑  收藏  举报