[13] 奇偶链表和相交链表

*题目来自leetcode

1.奇偶链表

题目要求

给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。

第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。

请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。

你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。

示例 1:

输入: head = [1,2,3,4,5]
输出: [1,3,5,2,4]

思路

这个题目看都看了一会才看懂,简单来说就是把链表中奇数位和偶数位区分开来,前面放原本的奇数位,后面放原本的偶数位。同时奇数位和偶数位中链表的前后顺序不发生变化。

考虑将每个偶数位用一个额外的链表进行保存,然后将每一个奇数位链接起来。最后奇数位的末尾链接偶数位。

# 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: Optional[ListNode]) -> Optional[ListNode]:
        if not head:
            return head
        double = ListNode(None)
        headd = double
        pointer = head
        while pointer:
            if pointer.next:
                double.next = ListNode(val=pointer.next.val)
                double = double.next
                if pointer.next.next:
                    poi = pointer.next.next
                    pointer.next = poi
                    pointer = pointer.next
                else:
                    break
            else:
                break
        pointer.next = headd.next
        return head

根据题目要求,时间复杂度和空间复杂度分别要求为O(n)和O(1),上述实现的时间复杂度满足,但是空间复杂度为O(n/2),也为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 oddEvenList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head:
            return head
        even = head.next
        evenhead = even
        odd = head

        while even and even.next:
            odd.next = even.next
            odd = odd.next
            even.next = odd.next
            even = even.next
        odd.next = evenhead
        return head

2.相交链表

题目要求

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

图示两个链表在节点 c1 开始相交:

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

注意,函数返回结果后,链表必须 保持其原始结构 。

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。

示例 :

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

思路:

最开始想的是AB同时遍历,遍历到一样的位置就是相交点,但是发现两个链表的长度不一定一致,导致遍历可能出现一前一后的情况,不能满足要求。后发现题目规定了两个链表中的值均大于0,所以有了新点子💡。遍历任意一个链表,把每个位置的值保存到一个列表中,并将原位的值修改为该值在列表中的位置坐标的负值。然后再遍历另一个链表,如果发现有负值存在则说明存在相交,退出遍历,返回当前链表指针。

这样做确实能够判断出两个链表是否相交,但是问题在于题目要求链表必须保持其原始结构,所以在判断完之后,无论是否相交,都要将第一个链表的值复原。

这样整体来看时间复杂度为O(2n+m),空间复杂度为O(n)。空间复杂度不满足题目的进阶要求。

官方给出的第一种方法是使用哈希表,大致思路与我写的思路类似,只不过官方存储的是将一个链表的节点存入哈希表,然后在这个哈希表中查询另一个链表的节点,这样就不需要修改链表的值。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        hashmap = []
        pointerA = headA
        while pointerA:
            hashmap.append(pointerA)
            pointerA = pointerA.next
        while headB:
            if hashmap.count(headB):
                value = 0
                break
            headB = headB.next
        return headB

第二种方法的思路是实际上就是解决了同时遍历存在的问题提出的,描述如下:

AB相交时,

链表 headA和 headB的长度分别是 m 和 n。假设链表 headA 的不相交部分有 a 个节点,链表 headB 的不相交部分有 b个节点,两个链表相交的部分有 c 个节点,则有 a+c=m,b+c=n。

如果 a=b,则两个指针会同时到达两个链表相交的节点,此时返回相交的节点;

如果 a≠b,则指针 pA 会遍历完链表 headA,指针 pB会遍历完链表 headB,两个指针不会同时到达链表的尾节点,然后指针 pA 移到链表 headB 的头节点,指针 pB\textit{pB}pB 移到链表 headA的头节点,然后两个指针继续移动,在指针 pA 移动了 a+c+b次、指针 pB\textit{pB}pB 移动了 b+c+a 次之后,两个指针会同时到达两个链表相交的节点,该节点也是两个指针第一次同时指向的节点,此时返回相交的节点。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        if headA == headB:
            return headB
        pointerA = headA
        pointerB = headB
        while pointerA or pointerB:
            if not pointerA:
                pointerA = headB
            else:
                pointerA = pointerA.next
            if not pointerB:
                pointerB = headA
            else:
                pointerB = pointerB.next
            if pointerB == pointerA:
                return pointerA

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值