代码随想录Day4: 链表Part2

Leetcode 24: 两两交换节点

讲解前:

这道题我觉得还是可以用一样的解法利用多个指针然后慢慢迭代来得到答案,这里因为我们最后return的时候还是要从head return然后为了方便解决头节点的edge case所以我也利用了一个dummy node,然后在纸上画出链表之后仔细过了一遍找到了解法,首先我们要确定的就是一定需要两个pointer分别是front 和 back来记录要swap的两个node,其次再需要这两个node前后的node,也就是prev和temp,先提前把front和temp连上,然后swap,再把prev和back连上,然后再进行遍历shift,把prev等于front然后front往后移动

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # create a dummy node in front 
        dummy = ListNode(0, head)

        # iterate through the list with front node
        front = head

        # have a prev node always point the node before front
        prev = dummy

        # only perform the swap when front node has another node after
        while front and front.next:

            # have a pointer to the back node
            back = front.next

            # have a pointer to keep the node after back
            temp = back.next

            # move the temp to be back the front now
            front.next = temp

            # make the back now point to front
            back.next = front

            # then connect the back with the prev
            prev.next = back

            # move the prev to front(which is now at the back after the swap)
            prev = front

            # then update the front to the next node 
            front = front.next

        return dummy.next

然后这里的循环条件要确保front和back都存在的情况下再进行swap也就是front和front.next都不能为None

讲解后:

看完卡哥的讲解之后发现卡哥很喜欢每次遍历都利用prev的位置去遍历和我不太一样,我喜欢循环中的条件和题目要求操作的node有关系,然后命名方面也是,卡哥有了temp 和temp1这种比较抽象的名,我更喜欢清楚一点的,卡哥的思路和我的没任何区别,虽然我的code中temp的角色可以删除掉直接用front.next.next来形容,但是我更喜欢分的清楚一些


Leetcode 19:删除倒数n节点

讲解前:

这道题我第一眼看到的时候觉得没什么难度,只是我们不能确定整个链表的长度at first所以我们先遍历一遍然后找到尾节点然后知道了总长度之后就能找到从后往前数第n的节点是哪了,然后非常简单的一个删除操作就可以了

class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        length = 0
        cur = head
        while cur:
            length = length + 1
            cur = cur.next
        
        # make the cur move to the node before the soon deleted one
        dummy = ListNode(0, head)
        cur = dummy
        for _ in range(length - n):
            cur = cur.next
        
        # perform the delete
        cur.next = cur.next.next

        return dummy.next

以上的代码就和我介绍的思路一样,我们为了deal edge case 当被删除的节点是头节点的问题,依然使用了一个dummy node,但是写完之后我总觉得这道题不应该就到此为止,一定有一种办法能够循环一遍就解决问题就像题目中进阶问题一样

我能想到的办法就是利用双指针,也就是说我们在遍历每一个节点的时候,cur扮演成一个慢指针的角色,然后有一个快指针去检查当前的慢指针是否指向了要被删除的node,也就是说在每一个慢指针的时候,直接去检查他后面n个节点是不是end of the list,如果是的话就直接删除就好了

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

        # iterate through the list with slow
        while slow:

            # for each slow, we check its next n node reached the end or not
            fast = slow
            for _ in range(n):
                fast = fast.next
            
            # checking if now the fast is at the end of the list
            if not fast.next:
                break
            else:
                slow = slow.next
        
        # after it got break, we do the deletion
        slow.next = slow.next.next

        # return the head with dummy.next
        return dummy.next

也就是我这里的解法,slow代表着当前遍历到的node,然后每一次我们都更新fast和slow一样然后然fast往后寻找n的node,如果fast指向了end of the list,那么就找到了倒数n个节点,这时我们就直接进行删除就好了,也用到了dummy node

讲解后:

卡哥和我的解法不一样!我看到他刚把思路说出来我就一下子茅塞顿开,对啊为什么我一开始没想到呢,我的代码中一次又一次让fast = slow的思路完全可以在代码初期就让fast先走远一点然后slow和fast一起移动就好了,这里按照卡哥的思路自己又写了一遍,相当于这道题一共写了三种解法,还是挺有意思的

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

        for _ in range(n):
            fast = fast.next
        
        while fast.next:
            slow = slow.next
            fast = fast.next

        slow.next = slow.next.next

        return dummy.next

Leetcode 面试题0207:链表相交

讲解前:

第一次看到Leetcode的题目中编号不是数字类型的,可能是第三方加入进去的?之前没有做过这道题,看到是简单决定自己先试一试,我一开始想的思路是两个链表如果相交的话相同的点就是他们从后往前遍历应该是一样的直到相交的那个node,所以我想的就是把两个链表翻转以后一个一个node比,比到不同为止,或者如果最后一个都不一样,直接return None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        # reverse the two list first
        reverse_head_a = reverse(headA)
        reverse_head_b = reverse(headB)
        cur_a, cur_b = ListNode(0, reverse_head_a), ListNode(0, reverse_head_b)

        if cur_a.next.val != cur_b.next.val:
            return None
            
        # iterate both list from behind
        while cur_a and cur_b:
            if cur_a.next.val == cur_b.next.val:
                cur_a = cur_a.next
                cur_b = cur_b.next
            else:
                break
        
        return cur_a
            

        return None

def reverse(head):
    cur = head
    prev = None

    while cur:
        temp = cur.next
        cur.next = prev

        prev = cur
        cur = temp

    return prev

但是我忘记了题目要求的是不能修改原数组,所以这种解法不可取,而且更严重的问题就是我们这里说的两个链表相交代表的是相交的那个node和之后的node在内存地址中也要是一样的,所以我一开始想的太简单了,但是其实相同之后解法并不难,只不过我需要用到extra memory目前,就把list a里面所有的node存在set里面就可以了,然后遍历list b 的node,第一个发现和set中匹配的就是相交的节点,如果没有一样的node,就return none就可以了

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        visited = set()
        cur_a = headA
        cur_b = headB
        while cur_a:
            visited.add(cur_a)
            cur_a = cur_a.next
        
        while cur_b:
            if cur_b not in visited:
                cur_b = cur_b.next
            else:
                return cur_b
        
        return None

我自己尝试想出空间复杂度为0(1) 的解法但是没能成功

讲解后:

 看完了题解以后不得不说这个方法真的很妙,同时其实也很好理解,真希望自己应该再多想一想或许还能想出解法

根据我这张图可以看出,有一个很重要的点我们需要理解就是最早的一个相交点,只能发生在长度较小的那个链表的第一个node,这样相交的部分就是整个较短的链表。相交点不可能出现在这之前,因为如果出现了的话,当我们走完整个较短的链表,长的那个链表还有元素,这不符合题目规定,所以我们要做的就是先把指针指向较长的那个链表的位置,这个位置刚好就是当从这里遍历到长链表结束是和短链表一样长,然后另一个指针正常指向短链表,然后我们开始一起移动直到找到一样的node就可以return了


Leetcode 142 环形链表 II

讲解前:

我记得之前做过环形链表的题,但是不记得是不是这个II,我印象中好像是也要通过双指针快和慢来解决问题,我打算自己先试一试

想了一会没有想出之前用的空间复杂度是O(1) 的解法,就先写了一个利用了哈希表的简单的方法,只需要把每一个遍历过的node 存到set里面,第一个发现的重复了的node就是环形开始的node

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # store all the visited node
        visited = set()

        cur = head
        while cur:
            if cur not in visited:
                visited.add(cur)
            else:
                return cur

            cur = cur.next
        
        return None 

这里我想到了之前解题的思路,其实如果链表的环形的话,有一个Gurantee就是,我们设置两个指针,一个快一个慢,一开始让快指针在前面,然后如果快指针每个循环移动两个node,慢指针只移动一个,这样的话,快指针一定会在未来追上慢指针,和慢指针指向同一个node,这样就可以保证这个链表中是环形的,但是我之前做的时候只需要确定是否是环形链表,但是不用找到环形开始的节点

讲解后:

看了卡哥的讲解之后豁然开朗,这道题运用到了数学推导公式我确实没有想到,在我们明白这是一个环形链表之后我们可以画一个这样的图然后进行推导

然后从这个图中我们可以这样推导一个公式

在这种情况下,推导出来x=z就意味着当我们找到了在哪一个node slow和fast相遇之后,这个节点到环形开始节点,和头节点到环形开始节点的距离是一样的,所以我们只需要在这个相遇节点和头节点再设置两个指针,让他俩一起移动,当他们相遇时,所在的那个节点就是我们环形链表中环形开始的节点了

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        slow = head
        fast = head

        while fast and fast.next:

            # move the slow and fast pointer
            slow = slow.next
            fast = fast.next.next

            # once the fast and slow point to the same node, we know it has a cycle
            if slow == fast:
                cur1 = head
                cur2 = fast

                # move pointer from head and where slow and fast met
                while cur1 != cur2:
                    cur1 = cur1.next
                    cur2 = cur2.next
                
                # once those two pointer point the same node, we found the result
                return cur1
        
        return None

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值