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

94 阅读5分钟

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

题目链接

leetcode.cn/problems/sw…

文章链接

programmercarl.com/0024.%E4%B8…

视频链接

www.bilibili.com/video/BV1YT…

看到题目的第一想法

我的第一想法很简单,我倒是很希望只交换值,但是这样做就违背了这道题的初衷,这道题和反转链表不一样,反转链表是全部反转,这道题是两两反转,也就是部分反转,之后我就去看了卡哥的视频。

看完代码随想录之后的想法

看了视频之后我恍然大悟,在纸上画一下便可知道,利用双指针,还是使用虚拟头结点来做这道题,从示例1可看出,1->2->3->4两两交换后就是2->1->4->3,dummyHead.Next将指针指向2,2再指向1,1指向3,然后移动双指针,dummyHead移动到2,然后继续相同的操作。最后就不难将代码写出来。下面附上我的go语言代码:

func swapPairs(head *ListNode) *ListNode {
        dummyHead := &ListNode{}
        dummyHead.Next = head
        cursor := dummyHead
        for cursor.Next != nil && cursor.Next.Next != nil {
            temp1 := cursor.Next
            temp2 := cursor.Next.Next.Next
            cursor.Next = cursor.Next.Next
            cursor.Next.Next = temp1
            cursor.Next.Next.Next = temp2
            cursor = cursor.Next.Next
        }
        return dummyHead.Next
}

实现过程中遇到的困难

这道题我写完了运行的时候发现超出时间限制,很懵逼,完全按照卡哥的思路写的,后来发现犯了一个低级错误,cursor = cursor.Next.Next这句话没有写。。。也就是说指针没有移动,就一直卡在循环里,加上这句话就好了,还有一个就是dummyHead.Next = head这句话当时也挺纠结,因为我最开始写的是head = dummyHead.Next,这句话运行出来结果始终为空,后来一想,如果是head = dummyHead.Next,dummyHead是临时定义的结点,其Next肯定是空,意思是把空结点赋值给head,head就置为空了,对空结点不管如何操作,其结果都是空。

今日收获

这道题更灵活,不要忘记移动指针了,还有就是多去思考。

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

题目链接

leetcode.cn/problems/re…

文章链接

programmercarl.com/0019.%E5%88…

视频链接

www.bilibili.com/video/BV1vW…

看到题目的第一想法

第一想法就是把链表先遍历一遍,定义一个count变量来记录结点的个数,第二次遍历的时候找到倒数第(n+1)个结点,才能找到倒数第n个结点,就能对其进行删除了。

看完代码随想录之后的想法

卡哥的方法令我眼前一亮,利用双指针法来解题,具体做法可以看卡哥的视频,视频链接在上方,还是用虚拟头结点来做,一个快指针,一个慢指针,快指针先移动n步,其次慢指针才开始移动,这时快慢指针同时移动,如果快指针的下一个结点是空,那么就可以删除满指针的下一个结点了,我写的代码和卡哥的思路有一点点区别,下面附上我的go语言代码:

func removeNthFromEnd(head *ListNode, n int) *ListNode {
    dummyHead := &ListNode{}
    dummyHead.Next = head
    fast, slow := dummyHead, dummyHead
    for i := n; i > 0; i-- {
        fast = fast.Next
    }
    for fast.Next != nil {
        slow = slow.Next
        fast = fast.Next
    }
    slow.Next = slow.Next.Next
    return dummyHead.Next
}

卡哥的思路的代码:

func removeNthFromEnd(head *ListNode, n int) *ListNode {
    dummyHead := &ListNode{}
    dummyHead.Next = head
    fast, slow := dummyHead, dummyHead
    for i := n; i >= 0; i-- {
        fast = fast.Next
    }
    for fast != nil {
        slow = slow.Next
        fast = fast.Next
    }
    slow.Next = slow.Next.Next
    return dummyHead.Next
}

区别大家可以自行观察。

实现过程中遇到的困难

刚刚那两段代码的区别就是我当时遇到的困难,fast和fast.Next,i > 0和i >= 0,这两个两两相互交换,删除的值都不对,最后我在纸上画了才想明白。

今日收获

还是要加强临界值的判断,细节决定成败!

面试题 02.07. 链表相交

题目链接

leetcode.cn/problems/in…

文章链接

programmercarl.com/%E9%9D%A2%E…

视频链接

本题无视频链接

看到题目的第一想法

第一想法就是分别求出两条链表的长度,长的链表比短的长n个结点,就让长链表先走n步,然后长短链表同时走,如果结点相等,即相交,如果走到空结点了,说明没有相交。下面附上我的go语言代码:

func getIntersectionNode(headA, headB *ListNode) *ListNode {
    if headA == nil || headB == nil {
        return nil
    }
    aHead, bHead := headA, headB
    countA, countB := 0, 0
    for aHead != nil {
        countA++
        aHead = aHead.Next 
    }
    for bHead != nil {
        countB++
        bHead = bHead.Next
    }
    result := subtract(countA, countB)
    aHead, bHead = headA, headB
    if countA > countB {
        for result != 0 {
            aHead = aHead.Next
            result--
        }
    } else if countA < countB {
        for result != 0 {
            bHead = bHead.Next
            result--
        }
    }
    for aHead != bHead {
        if aHead == nil {
            return nil
        }
        aHead = aHead.Next
        bHead = bHead.Next
    }
    return aHead
}

func subtract(a, b int) int {
    if a > b {
        return a - b
    }
    return b - a
}

看完代码随想录之后的想法

卡哥的想法和我的一样,这里就不再赘述了,官方还有一个双指针法,就是A链表和B链表先同时遍历,然后两者交换,A链表指针遍历B链表,B链表指针遍历A链表,如果相交,肯定会同时到达相交点,这个方法太妙了!国庆节抽时间写写并总结。

实现过程中遇到的困难

aHead, bHead := headA, headB这句话写了两遍(第二遍是=,不是:=),进行了两次赋值,因为第一次遍历后,指针和结点全变了,必须重新赋值才能进行第二次操作,最开始执行出错就是这个原因。

今日收获

双指针法很妙,有时间来写!

142.环形链表II

题目链接

leetcode.cn/problems/li…

文章链接

programmercarl.com/0142.%E7%8E…

视频链接

www.bilibili.com/video/BV1if…

看到题目的第一想法

不知道怎么求相交点,随后去看卡哥的视频了。

看完代码随想录之后的想法

这道题运用数学的方法,利用快慢指针,快指针一次走两步,慢指针一次走一步,如果有环,快指针会追上慢指针,先假设一个相遇点,最后通过数学证明(卡哥视频里面有),得出从起点到入环的第一个点的距离,和相遇点到入环的第一个点的距离相等,从这个关系来写代码,下面附上我的go语言代码:

func detectCycle(head *ListNode) *ListNode {
    if head == nil {
        return nil
    }
    fast, slow, temp := head, head, head
    for fast.Next != nil && fast.Next.Next != nil {
        slow = slow.Next
        fast = fast.Next.Next
        if slow == fast {
            for slow != temp {
                slow = slow.Next
                temp = temp.Next
            }
            return slow
        }
    }
    return nil
}

实现过程中遇到的困难

最开始for slow != head {}写掉了,导致代码陷入死循环,并且没有考虑链表是空的情况,于是将这两个地方进行了修改。

今日收获

这道题的数学证明过程有点繁琐,但是很有用,不然还不知道怎么做呢。