Day4代码随想录:24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、142.环形链表II、160. 链表相交

Day4:第二章 链表part02

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

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

具体思路就是三个位置的交换,要注意保存中间变量,可以手动画图。

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if head == None:
            return head
        dummyhead = ListNode()
        dummyhead.next = head
        cur = dummyhead
        while cur.next != None and cur.next.next != None:
            temp = cur.next #保存原始节点1的位置
            temp1 = cur.next.next.next #保存原始节点2的下一步指向--原始节点3
            cur.next = cur.next.next #pre指向原始节点2
            cur.next.next = temp #原始节点2指向原始节点1
            cur.next.next.next = temp1 #原始节点1指向原始节点3
            cur = cur.next.next #更新
        return dummyhead.next

这个递归版本非常简洁:

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if head is None or head.next is None:
            return head

        # 待翻转的两个node分别是pre和cur
        pre = head
        cur = head.next
        next = head.next.next
        
        cur.next = pre  # 交换
        pre.next = self.swapPairs(next) # 将以next为head的后续链表两两交换
         
        return cur

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

删除节点的注意事项:要得到删除的前一个节点

**思路:**分快慢指针,fast指针先移动n步,然后两个指针同时移动,直到最后一个节点,这样slow就到倒数第n位。因为要得到删除的前一个节点,因此fast再多移动一位,这样slow就正好到倒数n+1位,可以操作。

**证明:**为什么fast移动n步之后与slow一起移动可以正好得到倒数第n位:相当于进行了一个前后转移,fast指针是遍历了一遍链表总长度,slow指针是转移。m是链表长度,fast移动之后链表剩余(m-n)个需要移动,所以慢指针移动了(m-n)个,因此慢指针停在从后往前数m-(m-n) = n位

例如:[1 2 3 4 5],fast由1→2→3后,一起移动fast3→4→5→null,slow 1→2→3→4正好第4位是

如果链表长度是m,想找到倒数第k个节点,需要走(m+1)-(k+1) = m-k步,相当于5-2=3(从第一位是head来算,最后是在null)

class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        if head == None:
            return head
        dummyhead = ListNode()
        dummyhead.next = head
        fast = dummyhead
        slow = dummyhead
        for i in range(n+1): #首先移动n+1步快指针
            fast = fast.next
        while fast != None: #继续移动,直到快指针指向结尾
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next #删除操作
        return dummyhead.next

142.环形链表II

**思路:**设置一个快指针(以2个的速度移动),一个慢指针(以1个的速度移动)

分析为什么两者在环里会相交:因为相当于快指针以1个的速度多于慢指针前进追赶,所以会相交,如果快指针是多余2个或更多速度确实会错过慢指针

如何判断环的入口在哪:

相遇时: 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的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。当 n为1的时候,公式就化解为 x = z这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。 那么 n如果大于1是什么情况呢,就是fast指针在环形转n圈之后才遇到 slow指针。

其实这种情况和n为1的时候 效果是一样的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了(n-1)圈,然后再遇到index2,相遇点依然是环形的入口节点。

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 注意这题最后让返回的是节点,所以如果没有return None
        if head == None or head.next == None:
            return None
        fast = head
        slow = head
        while fast != None and fast.next != None:
            fast = fast.next.next
            slow = slow.next
            if fast==slow:
                index1 = slow
                index2 = head
                while index1 != index2:
                    index1 = index1.next
                    index2 = index2.next
                return index2
        return None

160. 链表相交

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

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值