链表
问:
- 判断一个单向链表是否有环,有的话返回入环节点,否则返回null
- 给定两个不知是否有环的单向链表,求它们是否相交,相交则返回相交的第一个节点,否则返回null
解:
- 快慢指针解法,快指针走两步,慢指针走一步,如果有环指针一定会相遇
function ringEntryNode(head) {
let slowIdx = head
let quickIdx = head
// 如果quick还可以走,并且未相遇就继续循环
while (quickIdx.next && (quickIdx !== slowIdx || quickIdx === head)) {
console.log(quickIdx);
quickIdx = quickIdx.next.next ? quickIdx.next.next : quickIdx.next
slowIdx = slowIdx.next
}
// quickIdx走到尾了
if (!quickIdx.next) return null
quickIdx = head
// quick返回头节点,接下来两指针都只移动一位,下次再相遇就是入环位置(这是定理,证明我不会)
while (quickIdx !== slowIdx) {
quickIdx = quickIdx.next
slowIdx = slowIdx.next
}
return slowIdx
}
- 首先判断这两个链表是否有环。分三种情况:
1).都无环,求无环单链表节点
2).有一个有环,那必定不相交
3).都有环,入环节点如果一样,那就等效于求无环单链表节点(把入环节点当作链表的尾),如果入环节点不一样,那么判断是否相交,若相交这两个入环节点都可以当作交点。
function noneRingIntersectNode(head1, head2, customTail = null) {
let size1 = 0
let size2 = 0
let cur1 = head1
let cur2 = head2
// 如果有自定义尾节点,那么到这里也停止
while (cur1 && cur1 !== customTail) {
cur1 = cur1.next
size1++
}
while (cur2 && cur2 !== customTail) {
cur2 = cur2.next
size2++
}
// 如果尾节点不相等,那么不相交
if (cur1 !== cur2) return null
// 计算链长度差值,两个指针回到链头部,把长的链多余的部分先走掉,然后两个指针一起走,第一次相遇就是交点
let moreLength = Math.abs(size1 - size2)
cur1 = head1
cur2 = head2
while (moreLength) {
size1 > size2 ? cur1 = cur1.next : cur2 = cur2.next
moreLength--
}
while (cur1 !== cur2) {
cur1 = cur1.next
cur2 = cur2.next
}
return cur1
}
function intersectNode(head1, head2) {
// 入环节点
const ringEntry1 = ringEntryNode(head1)
const ringEntry2 = ringEntryNode(head2)
let cur = ringEntry1
// 第一种情况,都是无环链表
if (!ringEntry1 && !ringEntry2) {
return noneRingIntersectNode(head1, head2)
}
// 第二种情况,一个有环一个无环,那么必定不相交
if ((!ringEntry1 && ringEntry2) || (ringEntry1 && !ringEntry2)) return null
// 第三种情况,都有环。这种情况有又分三种
// 同一节点入环,实际上就可以看作单链表相交的问题,入环点看作链表的尾节点
if (ringEntry1 === ringEntry2) {
return noneRingIntersectNode(head1, head2, ringEntry1)
}
// 不同节点入环
// 从入环节点开始遍历链表一,若两个链表相交,那么走完一次环的过程中,cur必定会遇到ringEntry2
while (cur) {
cur = cur.next
if (cur === ringEntry1 || cur === ringEntry2) break
}
// 遍历完一次链表一的环,如果cur没遇到ringEntry2,就表明不相交。如果遇到了那么随便返回一个入环点都算交点
return cur !== ringEntry2 ? null : ringEntry1
}