如果一个链表中包含环,如何找出环的入口节点?本文将分享一种解决方案,欢迎各位感兴趣的开发者阅读本文。
我们通过一个例子来做进一步的分析:
首先,我们需要确保链表中是否包含一个环,在上篇文章(获取链表中倒数第K个节点)中我们用双指针的思路解决了问题,那么,我们也尝试下能否用双指针来解决这个问题。
定义两个指针,从链表的头节点出发
IMG_C6505EF145D3-1
我们来观察下这个有环链表,将两个指针都指向链表头部。环中有4个节点,那么
IMG_66D663B2FE91-1
通过上个章节的分析,我们知道了只要能得到环中的节点数量,就可以找到环的入口节点。在前面提到的判断一个链表中是否有环时用到了一快一慢两个指针。如果两个指针相遇,则表明链表中存在环。
我们可以从它们相遇的节点出发,一边继续向前移动一边计数,当再次回到这个节点时,就可以得到环中节点数了。
IMG_584FEB598A64-1
通过上面的分析,我们已经得到了解决问题的思路,接下来,我们来看下代码实现。
这里我们基于上篇文章所创建的类,扩展一个名为findRingEntranceNode
的方法,实现寻找链表中环的入口节点函数:
// 寻找环的入口节点
findRingEntranceNode(): ListNode | null {
// 初始化两个指针指向
this.pNext = this.listHead;
this.pHead = this.listHead;
let hasRing = false;
while (this.pNext.next) {
// p1指针每次走1步
this.pNext = this.pNext.next;
// p2指针每次走2步
let step = 2;
while (this.pHead.next) {
if (step > 0) {
this.pHead = this.pHead.next;
step--;
}
if (step === 0) {
break;
}
}
// p1、p2相遇, 链表中就包含环
if (this.pNext === this.pHead) {
hasRing = true;
break;
}
}
// 链表中有环
if (hasRing) {
let ringCount = 0;
// 获取环中节点数量
while (this.pNext.next) {
ringCount++;
this.pNext = this.pNext.next;
// p1、p2相遇,得到环中节点总数量
if (this.pHead === this.pNext) {
break;
}
}
// 寻找环的入口节点
while (this.pNext.next) {
// 移动p1指针ringCount步
this.pNext = this.pNext.next;
ringCount--;
if (ringCount === 0) {
// 重置p2指针位置到链表头部
this.pHead = this.listHead;
break;
}
}
// p1、p2指针以相同的速度向前移动
while (this.pNext.next) {
this.pNext = this.pNext.next;
if (this.pHead.next) {
this.pHead = this.pHead.next;
}
// p1、p2相遇的节点正好是环的入口节点
if (this.pNext === this.pHead) {
return this.pNext;
}
}
return this.pNext;
}
// 链表中不存在环
return null;
}
完整代码请移步👉:GetLinkedListNode.ts
接下来,我们将前言中的例子代入上个章节所实现的函数中,验证下它能否得出正确的结果。
const linkedList = new LinkedList();
linkedList.push(1);
linkedList.push(3);
linkedList.push(8);
linkedList.push(9);
linkedList.push(12);
linkedList.push(18);
// 修改最后一个节点的指针指向至链表的第3个节点,构造一个有环链表
linkedList.getElementAt(linkedList.size() - 1).next =
linkedList.getElementAt(2);
const getLinkedListNode = new GetLinkedListNode(linkedList.getHead());
resultNode = getLinkedListNode.findRingEntranceNode();
console.log("环的入口节点", resultNode);
运行结果如下所示,跟我们在思路分析章节中所得到的结果一致。
image-20220611075239243
完整代码请移步👉:GetLinkedListNode-test.ts
注意:上述代码中用到的LinkedList是自定义的一个类,它实现了链表这个数据结构,对其原理感兴趣的开发者请移步我的另一篇文章👉:链表与变相链表的实现。
本文所列举的代码,其完整版请移步👇:
至此,文章就分享完毕了。
我是神奇的程序员,一位前端开发工程师。
如果你对我感兴趣,请移步我的个人网站,进一步了解。