代码随想录算法训练营Day4 | 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II、链表总结 | Python | 个人记录向

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

代码随想录:24. 两两交换链表中的节点
Leetcode:24. 两两交换链表中的节点

直接做题

自己推导,不用虚拟头结点,基本OK,但发现[1, 2, 3, 4]输出后变成了[2, 1, 3],看了代码随想录,感觉思路不一样,还是自己捣鼓了一下,发现是没有将1索引到4。最终结果如下:

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head or not head.next:
            return head
        
        pre = head
        cur = head.next
        new_head = cur
        while True:
            temp = cur.next
            pre.next = temp
            cur.next = pre
            if temp and temp.next:
                pre.next = temp.next # 解决1索引到4
                pre = temp
                cur = temp.next
            else:
                return new_head

看文章

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

虚拟结点

相当于把我的初始边界条件判断,用虚拟节点解决了。但我感觉反而比较难理解,具体见“个人注释”。

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        dummy_head = ListNode(next=head)
        current = dummy_head
        
        # 必须有cur的下一个和下下个才能交换,否则说明已经交换结束了
        while current.next and current.next.next:
            temp = current.next # 防止节点修改
            temp1 = current.next.next.next
            
            current.next = current.next.next
            current.next.next = temp  # “个人注释”:因为上一行,这里其实cur已经被改变了
            temp.next = temp1
            current = current.next.next
        return dummy_head.next

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

递归

比起上面,这个反而更好理解,它在处理头结点的思路和我一样。

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个节点

代码随想录:19.删除链表的倒数第N个节点
Leetcode:19.删除链表的倒数第N个节点

做题

考虑快慢节点,但是没想出来。看了代码随想录,fast比slow多走n+1步,且设置虚拟头结点。但代码随想录中的python代码没有考虑fast.next为none的情况,不知道为什么ac了。我看完修改的代码如下:

class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy_head = ListNode(0, next=head)

        slow = fast = dummy_head
        for i in range(n+1):
            fast = fast.next
            if not fast and i < n:
                return dummy_head.next
            
        while fast:
            fast = fast.next
            slow = slow.next

        slow.next = slow.next.next
        return dummy_head.next

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

面试题 02.07. 链表相交

代码随想录:面试题 02.07. 链表相交
Leetcode:面试题 02.07. 链表相交

直接做题

没想出怎么用时间复杂度O(n)、仅用O(1)内存的方案。

看文章

思路如下:

  1. 先分别求A、B的长度,然后使lenA < lenB(不满足则互换)。
  2. 将指针移动至lenB - lenA的位置上,使两个链表尾部对齐。
  3. 指针同时移动,如果指针位置的val相等,则返回val,否则继续移动,直至结束。
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        lenA, lenB = 0, 0
        cur = headA
        while cur:         # 求链表A的长度
            cur = cur.next 
            lenA += 1
        cur = headB 
        while cur:         # 求链表B的长度
            cur = cur.next 
            lenB += 1
        curA, curB = headA, headB
        if lenA > lenB:     # 让curB为最长链表的头,lenB为其长度
            curA, curB = curB, curA
            lenA, lenB = lenB, lenA 
        for _ in range(lenB - lenA):  # 让curA和curB在同一起点上(末尾位置对齐)
            curB = curB.next 
        while curA:         #  遍历curA 和 curB,遇到相同则直接返回
            if curA == curB:
                return curA
            else:
                curA = curA.next 
                curB = curB.next
        return None 

时间复杂度:O(n + m)
空间复杂度:O(1)
思路是能看明白,但我对时间复杂度的理解有误,实际上应该是遍历了小于等于2n+2m次,但时间复杂度确实为O(n + m)。

142.环形链表II

代码随想录:142.环形链表II
Leetcode:142.环形链表II

做题

快慢指针,快指针每次走两步,慢指针每次走一步,可以找到环。但环的位置不会找。

看文章

环的位置:两个指针分别从起点、相遇处出发,再次相遇时,即可通过前者找到环的位置。

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        slow = head
        fast = head
        
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            
            # If there is a cycle, the slow and fast pointers will eventually meet
            if slow == fast:
                # Move one of the pointers back to the start of the list
                slow = head
                while slow != fast:
                    slow = slow.next
                    fast = fast.next
                return slow
        # If there is no cycle, return None
        return None

时间复杂度: O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n
空间复杂度: O(1)

以往忽略的知识点小结

  • 多用虚拟节点
  • 找到倒数第n个点,可以通过fast比slow先走n+1步
  • 反复k次遍历一个长度为n的链表,只要k << n,一般就视其时间复杂度为O(n)
  • 快慢指针,也可以是步幅快
  • 环形链表,需要推导规律,找环为追及问题,找环的位置就从起点、追及点同步往前走

个人体会

完成时间:3h。
心得:思路越来越丰富,还是要慢慢积累、循序渐进,有时间的话要静下心多想想。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值