2021-04-10:给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返回null。【要求】如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度 请达到O(1)。
福大大 答案2021-04-10:
1.获取head1和head2的第一个入环节点。
2.head1和head2环节点的3种情况。
2.1.如果head1和head2只有其中一个有环,直接返回false。
2.2.如果head1和head2都没环。双指针,见力扣【剑指 Offer 52. 两个链表的第一个公共节点】。
2.3.如果head1和head2都有环。精髓在这里。
2.3.1.head1和head2根据入环节点分别断成两个链表。
2.3.2.head1左部分和head2左部分,根据2.2步骤求交点。保存在ans中。
2.3.3.head1和head2左右部分分别合并。
2.3.如果ans为空,需要循环判断head1的入环节点,如果循环了一圈都没找到head2中的入环节点,ans肯定为空;如果找到了,ans为head1的入环节点。
3.返回ans。
代码用golang编写。代码如下:
package main import "fmt" func main() { head1 := &ListNode{Val: 1} head1.Next = &ListNode{Val: 2} head1.Next.Next = &ListNode{Val: 3} head1.Next.Next.Next = &ListNode{Val: 4} head1.Next.Next.Next.Next = &ListNode{Val: 5} head1.Next.Next.Next.Next.Next = &ListNode{Val: 6} head1.Next.Next.Next.Next.Next.Next = head1.Next.Next head2 := &ListNode{Val: 7} head2.Next = &ListNode{Val: 8} head2.Next.Next = head1.Next.Next.Next.Next ret := getIntersectionNode(head1, head2) fmt.Println(ret) } //Definition for singly-linked list. type ListNode struct { Val int Next *ListNode } func getIntersectionNode(head1, head2 *ListNode) *ListNode { if head1 == nil || head2 == nil { return nil } loop1 := GetLoopNode(head1) loop2 := GetLoopNode(head2) if loop1 == nil && loop2 == nil { return NoLoop(head1, head2) } if loop1 != nil && loop2 != nil { return BothLoop(head1, loop1, head2, loop2) } return nil } //获取入环节点 func GetLoopNode(head *ListNode) *ListNode { if head.Next == nil || head.Next.Next == nil { return nil } slow := head.Next fast := head.Next.Next for slow != fast { if fast.Next == nil || fast.Next.Next == nil { return nil } fast = fast.Next.Next slow = slow.Next } fast = head for slow != fast { slow = slow.Next fast = fast.Next } return slow } //没有入环节点 func NoLoop(head1 *ListNode, head2 *ListNode) *ListNode { cur1 := head1 cur2 := head2 for i := 0; i < 3; i++ { for cur1 != nil && cur2 != nil { if cur1 == cur2 { return cur1 } cur1 = cur1.Next cur2 = cur2.Next } if cur1 == nil { cur1 = head2 } else if cur2 == nil { cur2 = head1 } } return nil } //有入环节点 func BothLoop(head1 *ListNode, loop1 *ListNode, head2 *ListNode, loop2 *ListNode) *ListNode { loop1Next := loop1.Next loop2Next := loop2.Next //head1和head2根据入环节点分别断成两个链表。 loop1.Next = nil loop2.Next = nil //head1左部分和head2左部分,根据2.2步骤求交点。保存在ans中。 ans := NoLoop(head1, head2) //head1和head2左右部分分别合并。 loop1.Next = loop1Next loop2.Next = loop2Next //如果ans为空,需要循环判断head1的入环节点,如果循环了一圈都没找到head2中的入环节点,ans肯定为空;如果找到了,ans为head1的入环节点。 if ans == nil { loop1Copy := loop1.Next for loop1Copy != loop1 { if loop1Copy == loop2 { ans = loop1 break } loop1Copy = loop1Copy.Next } } //返回ans。 return ans }
执行结果如下: