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 {}写掉了,导致代码陷入死循环,并且没有考虑链表是空的情况,于是将这两个地方进行了修改。
今日收获
这道题的数学证明过程有点繁琐,但是很有用,不然还不知道怎么做呢。