链表—Python


2. 两数相加

给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储一位数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头
示例:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

# 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:
        dummy = ListNode(0)
        p = dummy
        carry = 0
        while l1 or l2:
            if l1 and l2:
                value = l1.val + l2.val + carry
            elif l1:
                value = l1.val + carry
            elif l2:
                value = l2.val + carry
            carry = 1 if value > 9 else 0
            value = value - 10 if value > 9 else value
            p.next = ListNode(value)
            p = p.next
            l1 = l1.next if l1 else l1
            l2 = l2.next if l2 else l2
        if carry == 1:
            p.next = ListNode(1)
        return dummy.next

复杂度分析

时间复杂度:O(max(m,n)),其中 m 和 n 分别为两个链表的长度。

空间复杂度:O(1)。

21. 合并两个有序链表

21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

  • 方法一(迭代):时间复杂度O(n+m),空间复杂度O(1)
# 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, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        head = ListNode(-1)
        p = head
        while list1 and list2:
            if list1.val <= list2.val:
                p.next = list1
                list1 = list1.next
            else:
                p.next = list2
                list2 = list2.next
            p = p.next

        p.next = list1 if list1 != None else list2
        return head.next             
  • 方法二(递归):时间复杂度O(n+m),空间复杂度O(n+m)
class Solution():
    def mergeTwoLists(self,list1,list2):
        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个升序链表

23. 合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。

  • heapq 实现的优先级队列
# 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:
        import heapq
        dummy = ListNode(0)
        p = dummy
        head = []
        for i in range(len(lists)):
            if lists[i] :
                heapq.heappush(head, (lists[i].val, i))
                lists[i] = lists[i].next
        while head:
            val, idx = heapq.heappop(head)
            p.next = ListNode(val)
            p = p.next
            if lists[idx]:
                heapq.heappush(head, (lists[idx].val, idx))
                lists[idx] = lists[idx].next
        return dummy.next

时间复杂度:优先队列 pq 中的元素个数最多是 k,所以一次 pop 或者 push 方法的时间复杂度是 O(logk);所有的链表节点都会被加入和弹出 pq,这里最多有 k n kn kn 个点,对于每个点都被插入删除各一次,故总的时间代价即渐进时间复杂度为 O ( k n × log ⁡ k ) O(kn \times \log k) O(kn×logk)

空间复杂度:优先队列中的元素不超过 k k k个,故渐进空间复杂度为 O ( k ) O(k) O(k)


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

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

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

  • 时间复杂度 O(N)
# 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:
        p1 = head
        for i in range(k):
            p1 = p1.next
        p2 = head
        while p1:
            p1 = p1.next
            p2 = p2.next
        return p2

面试题 02.02. 返回倒数第 k 个节点

面试题 02.02. 返回倒数第 k 个节点

实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

注意:与上题思路一致,只是函数返回的第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:
        # 指针 p1 指向链表的头节点 head
        p1 = head
        for i in range(k):
            p1 = p1.next
        p2 = head
        while p1:
            p1 = p1.next
            p2 = p2.next
        return p2.val

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

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

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

示例 :

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
# 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:
        dummy = ListNode()
        p = dummy
        while head.val != val:
            p.next = head
            head = head.next
            p = p.next
        p.next = head.next
        return dummy.next

237. 删除链表中的指定节点

237. 删除链表中的指定节点

请编写一个函数,用于 删除单链表中某个特定节点 。在设计函数时需要注意,你无法访问链表的头节点 head ,只能直接访问 要被删除的节点 。

题目数据保证需要删除的节点 不是末尾节点

输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:指定链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9
  • 思路:和下一个节点交换,再删除下一个节点
  • 时间复杂度:O(1),空间复杂度:O(1),主要的代码就两行。
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteNode(self, node: ListNode):
        """
        :rtype: void Do not return anything, modify node in-place instead.
        """
        node.val = node.next.val
        node.next = node.next.next

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

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

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

# 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:
        dummy = ListNode(0,head)
        p1 = head
        for i in range(n):
            p1 = p1.next
        p2 = dummy
        while p1:
            p1 = p1.next
            p2 = p2.next
        p2.next = p2.next.next
        return dummy.next

复杂度分析

时间复杂度:O(N),其中 N为链表的长度。

空间复杂度:O(1)。


876. 链表的中间结点

876. 链表的中间结点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

  • 思路:

如果想一次遍历就得到中间节点,可以使用「快慢指针」。两个指针 slowfast 分别指向链表头结点 head每当慢指针 slow 前进一步,快指针 fast 就前进两步,这样,当 fast 走到链表末尾时,slow 就指向了链表中点

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

2095. 删除链表的中间节点

2095. 删除链表的中间节点

给你一个链表的头节点 head 。删除 链表的 中间节点 ,并返回修改后的链表的头节点 head

长度为 n 链表的中间节点是从头数起第 ⌊n / 2⌋ 个节点(下标从 0 开始),其中 ⌊x⌋ 表示小于或等于 x 的最大整数。

对于 n = 1、2、3、4 和 5 的情况,中间节点的下标分别是 0、1、1、2 和 2 。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def deleteMiddle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if head.next == None:
            return
        fast = head
        slow = head
        while fast and fast.next:
            fast = fast.next.next
            pre = slow
            slow = slow.next
        
        pre.next = slow.next
        return head


面试题 02.01. 移除重复节点

面试题 02.01. 移除重复节点

编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。

# 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:
        if head == None or head.next == None:
            return head
        
        dummy = ListNode(0,head)
        pre = dummy
        p = head
        memo = []
        while p:
            if p.val in memo:
                p = p.next
                pre.next = p
            else:
                memo.append(p.val)
                p = p.next
                pre = pre.next
        return dummy.next
  

141. 环形链表

141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

  • 解决方案同求链表的中间节点也是使用快慢指针:

每当慢指针 slow 前进一步,快指针 fast 就前进两步。如果 fast 最终遇到空指针,说明链表中没有环;如果 fast 最终和 slow 相遇,那肯定是 fast 超过了 slow 一圈,说明链表中含有环。

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

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

142. 环形链表 II

142. 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

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

        slow = head
        while slow != fast:
            fast = fast.next
            slow = slow.next
        return slow

160. 相交链表

160. 相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

题目数据 保证 整个链式结构中不存在环。

  • 思路:

可以让 p1 遍历完链表 A 之后开始遍历链表 B,让 p2 遍历完链表 B 之后开始遍历链表 A,这样相当于「逻辑上」两条链表接在了一起。

如果这样进行拼接,就可以让 p1p2 同时进入公共部分,也就是同时到达相交节点 c1

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

206. 反转链表

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

方法一:递归反转链表

  • 递归函数的定义:输入一个节点 head,将「以 head 为起点」的链表反转,并返回反转之后的头结点
  • 时间复杂度:O(n),其中 n是链表的长度;空间复杂度:O(n)。
# 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 or head.next == None:
            return head
        
        last = self.reverseList(head.next)
        head.next.next = head
        head.next = Non
        return last

方法二:迭代反转链表

  • 时间复杂度:O(n),其中 n是链表的长度;空间复杂度:O(1)。

请添加图片描述

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if head == None or head.next == None:
            return head

        p = head
        # 反转后尾节点的next为None
        dummy = None 
        while p:
            curr = p.next
            p.next = dummy
            dummy = p
            p = curr
        return dummy

反转链表前N个节点

def reverseN(head, n):
        if n == 1:
            return head   
        dummy = None
        pre = head
        p = head
        for i in range(n):
            curr = p.next
            p.next = dummy
            dummy = p
            p = curr

        pre.next = p
        return dummy

92. 反转链表 II

92. 反转链表 II

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

方法一:递归

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
        if left == 1:
            return self.reverseN(head,right)
        head.next = self.reverseBetween(head.next,left-1,right-1)
        return head
    
    def reverseN(self, head, n):
        if n == 1:
            return head   
        dummy = None
        pre = head
        p = head
        for i in range(n):
            curr = p.next
            p.next = dummy
            dummy = p
            p = curr

        pre.next = p
        return dummy

方法二:「穿针引线」反转链表(头插法)

  • 时间复杂度:O(N),其中 N 是链表总节点数。
  • 空间复杂度:O(1)
class Solution:
    def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
        dummy = ListNode()
        dummy.next = head
        pre = dummy
        for i in range(1,left):
            pre = pre.next

        p = pre.next
        for _ in range(right - left):
            curr = p.next
            p.next = curr.next
            curr.next = pre.next # 注意此处不能写 curr.next = p         
            pre.next = curr

        return dummy.next

25. K 个一组翻转链表

25. K 个一组翻转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        p1 = head
        p2 = head
        for i in range(k):
            if p1:
                p1 = p1.next
            else:
                return head
        newHead = self.reverseN(head,k)
        p2.next = self.reverseKGroup(p1, k)# 反转后p2是前k个节点的尾节点

        return newHead
    
    def reverseN(self, head, n):
        if n == 1:
            return head
        dummy = None
        p = head
        pre = head
        for i in range(n):
            curr = p.next
            p.next = dummy
            dummy = p
            p = curr

        pre.next = p
        return dummy

复杂度分析

  • 时间复杂度:O(N),其中 N 是链表总节点数。
  • 空间复杂度:O(1)

1721. 交换链表中的节点

1721. 交换链表中的节点

给你链表的头节点 head 和一个整数 k

交换 链表正数第 k 个节点和倒数第 k 个节点的值后,返回链表的头节点(链表 从 1 开始索引)。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        if head == None or head.next == None:
            return head
        p1 = head
        for i in range(1,k):
            p1 = p1.next
        curr = p1
        val1 = curr.val
        p2 = head
        while p1.next:
            p1 = p1.next
            p2 = p2.next
        val2 = p2.val

        p2.val = val1
        curr.val = val2
        return head


234. 回文链表

234. 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false

方法一:把原始链表反转存入一条新的链表,然后比较这两条链表是否相同。

  • 时间复杂度:O(N),其中 N 是链表总节点数;空间复杂度:O(N)。

方法二:借助二叉树后序遍历的思路,不需要显式反转原始链表也可以倒序遍历链表。实际上就是把链表节点放入一个栈,然后再拿出来,这时候元素顺序就是反的

  • 时间复杂度:O(N),空间复杂度:O(N)。
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        self.left = head
        return self.traverse(head)

    def traverse(self, head):
        if head == None:
            return True
        
        res = self.traverse(head.next)
        res = (res and self.left.val == head.val)
        self.left = self.left.next
        return res

方法三:优化空间复杂度

时间复杂度:O(N),空间复杂度:O(1)。

思路:

1、先通过快慢指针来找到链表的中点

2、如果fast指针没有指向null,说明链表长度为奇数,slow还要再前进一步

3、从slow开始反转后面的链表,再比较中点左右是否相同就可以了

class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        if head == None or head.next == None:
            return True
        fast = head
        slow = head
        left =head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        if fast:
            slow = slow.next

        right = self.converse(slow)
        while right:
            if left.val != right.val:
                return False
            left = left.next
            right = right.next
        return True

    def converse(self,head):
        if head == None or head.next == None:
            return head

        dummy = None
        p = head
        while p:
            curr = p.next
            p.next = dummy
            dummy = p 
            p = curr
        return dummy
  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

葫芦娃啊啊啊啊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值