链表 专项

本文介绍了链表相关的经典算法问题,包括合并两个有序链表、合并K个升序链表、环形链表检测与定位、链表中点查找、相交链表判断、删除指定节点、两数相加、两两交换节点、旋转链表、删除重复元素等。通过快慢指针、优先队列等方法实现高效解决方案。
摘要由CSDN通过智能技术生成

https://leetcode-cn.com/tag/linked-list/problemset/

21. 合并两个有序链表(简单)

https://leetcode-cn.com/problems/merge-two-sorted-lists/
「虚拟头结点」

# 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 l1 == None and l2 == None:
            return None
        pre = ListNode()
        pre.next = l1 if l1 != None else l2
        root = pre
        while l1 and l2:
            if l1.val <= l2.val:
                pre.next = l1
                pre = l1
                l1 = l1.next
            else:
                pre.next = l2
                pre = l2
                l2 = l2.next
        if l1:
            pre.next = l1
        if l2:
            pre.next = l2
        return root.next

23. 合并K个升序链表(困难)

https://leetcode-cn.com/problems/merge-k-sorted-lists/

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        dummy = ListNode()
        p = ListNode()
        dummy.next = p

        #当lists里面全部为空节点时,循环结束
        while any(lists):
            #寻找此时头节点最小的链表
            minValue = float(inf)
            for i in range(len(lists)):
                if lists[i] == None:
                    pass
                else:
                    if lists[i].val < minValue:
                        minValue = lists[i].val
                        minIndex = i
            #此时头节点最小的链表为lists[minIndex]
            p.next = lists[minIndex]
            p = lists[minIndex] 
            lists[minIndex] = lists[minIndex].next
        return dummy.next.next
VOC 2007 Error Analysis

上面算法的问题在于时间复杂度比较高,因为对于在找头节点最小的链表时,是依次遍历。如果在这儿进行优化的话,可以维护一个优先级队列(更多信息可参见:https://docs.python.org/3/library/queue.html),把链表节点放入一个最小堆,就可以维护topk最小的节点,时间复杂度为o(N*logk) 代码如下:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
from queue import PriorityQueue
class Solution:
    
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        dummy = ListNode()
        p = dummy
        priorityQ = PriorityQueue()
        #遍历lists
        for i in range(len(lists)):
            if lists[i]:
                priorityQ.put((lists[i].val,i,lists[i]))  
				"""
				#注意优先队列需要进行比较,来判断插入位置)(默认从小到大),因此不能直接传node(不可比较),
				也不能传(node.val, node)因为可能存在val相等的两个node,
				因此加入一个index i来辅助比较,先比较val,如果相等,再比较i
				"""
        #对优先队列进行get与put
        while not priorityQ.empty():  #注意这儿判断是否为空,用内置empty函数
            val,i,node = priorityQ.get()
            p.next = node
            p = node
            if node.next:
                priorityQ.put((node.next.val,i,node.next))
        return dummy.next

141. 环形链表(简单)

https://leetcode-cn.com/problems/linked-list-cycle/
快慢指针即可

# 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:
        if head == None:
            return False
        slow = head
        fast = head.next
        while fast and fast != slow:
            slow = slow.next
            if fast.next:
                fast = fast.next.next
            else:
                fast = None
                break
        if fast:
            return True
        else:
            return False

142. 环形链表 II(中等)

https://leetcode-cn.com/problems/linked-list-cycle-ii/
这是上面那一道题目的扩展,值得注意的是,这道题得让slow与fast指针同时从head出发,而不能像上一题一样,一个在后一个在前

# 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:
        slow = head
        fast = head
        #fast与slow相遇,为相遇点
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if fast == slow:
                break
        if fast == None or fast.next == None:
            return None
        #slow从head出发,fast从相遇点出大,各自步长都为1,向前走
        slow = head
        while slow != fast:
            slow = slow.next
            fast = fast.next
        #当再一次相遇时,即为环口
        return fast

图示:
VOC 2007 Error Analysis
值得注意的一点是,不要纠结于距离,而是着眼于走多少步,就可以避免多绕的圈带来的思维上的困惑。

876. 链表的中间结点(简单)

https://leetcode-cn.com/problems/middle-of-the-linked-list/
最基础快慢指针

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
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

160. 相交链表

https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
最naive的想法是,分别求出两个链表的长度,然后让长的链表多走s步(两者长度的差值),此时两个链表在同一起跑线,如果中间有相同的节点,则相交

# 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:
        a = headA
        b = headB
        #先遍历A的长度
        la = 0
        while a:
            la += 1
            a = a.next
        #再遍历B的长度
        lb = 0
        while b:
            lb += 1
            b = b.next
        a = headA
        b = headB
        if lb > la:
            for i in range(lb - la):
                b = b.next
        else:
            for i in range(la - lb):
                a = a.next
        while a != b:
            a = a.next
            b = b.next
        return a  #相交或者走到none都包含在内

上面这种解法等于是两个链表分别遍历了两遍
但是写得有点啰嗦,有没有更加优雅的写法呢?

# 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:
        a = headA
        b = headB
        while a != b:
            if a == None:
                a = headB
            else:
                a = a.next
            if b == None:
                b = headA
            else:
                b = b.next
        return a

上面等于是强行让两个链表长度一样,如果相交,则必然中间会遇到相同的节点,本质上和上面的时间复杂度是一样的

19. 删除链表的倒数第 N 个结点(中等)

https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        p1 = head
        p2 = ListNode()  #注意虚节点的应用
        p2.next = head
        for i in range(n):
            p1 = p1.next
        while p1:
            p1 = p1.next
            p2 = p2.next

        if p2.next == head:
            return head.next
        p2.next = p2.next.next
        return head
        

2. 两数相加(中等)

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

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        p1 = l1
        p2 = l2
        p = ListNode()
        res = p
        div = 0
        while p1 or p2: #注意这个or
            p1_v = 0 if p1 == None else p1.val
            p2_v = 0 if p2 == None else p2.val
            div,mod = divmod((p1_v+p2_v+div),10) #divmod同时返回商和余数
            q = ListNode(mod)
            p.next = q
            p = q
            p1 = p1.next if p1 != None else None
            p2 = p2.next if p2 != None else None
        #边界条件要考虑到
        if div:
            q = ListNode(1)
            p.next = q
        return res.next

24. 两两交换链表中的节点(中等)

https://leetcode-cn.com/problems/swap-nodes-in-pairs/
迭代法

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        if head == None:
            return None
        pre = ListNode() #需要一个pre指针保留前面的结果
        res = pre
        p = head #p,q分别是要交换的节点指针
        q = p.next
        pre.next = p
        
        while q:
            #此时交换p,q
            p.next = q.next
            q.next = p
            pre.next = q

            #重新置pre,p,q的值
            pre = p
            p = p.next
            q = p.next if p else None
        return res.next

递归法

61. 旋转链表(中等)

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

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        if k == 0 or head == None:
            return head
        #首先计算链表的长度
        p = head
        n = 0
        while p:
            n += 1
            p = p.next
        k = k%n + 1
        p1 = head
        p2 = head
        while k:
            p1 = p1.next
            k -= 1
        while p1:
            p1 = p1.next
            p2 = p2.next
        pre = p2
        p = pre.next
        pre.next = None
        pre = p
        if p == None:
            return head
        while p.next:
            p = p.next
        p.next = head
        return pre 

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

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

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if head == None:
            return head
        pre = head
        p = head.next
        while p:
            if p.val != pre.val:
                pre.next = p
                pre = p
            p = p.next
        pre.next = None
        return head

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

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

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        dumpy = ListNode()
        dumpy.next = head
        if head == None:
            return head
        pre = dumpy
        slow = head
        fast = head.next
        while fast:
            if fast.val != slow.val:
                if slow.next == fast:
                    pre = slow
                else:
                    pre.next = fast
                slow = fast
            fast = fast.next
        if slow.next:
            pre.next = fast
        return dumpy.next

206. 反转链表(简单)

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

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if head == None:
            return head
        slow = head
        fast = head.next
        slow.next = None
        while fast:
            p = fast.next
            fast.next = slow
            slow = fast
            fast = p
        return slow

203. 移除链表元素(简单)

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

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        slow = ListNode(-1)
        fast = head
        pre = slow
        pre.next = head
        while fast:
            if fast.val == val:
                slow.next = fast.next
            else:
                slow = fast
            fast = fast.next
        return pre.next           
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猴猴猪猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值