判断链表有环&求循环列表环长&求循环列表入环点
关于更多的链表实现可以从下方直通车达到:
单向链表
双向链表
循环链表
本篇希望着重介绍的是结合数学中的追及问题与循环列表来达到判断列表有环和求环长
首先我们第一个问题来关注如何判断列表有环
这是一个基本的链表,最简便的方法就是用一个下标P来遍历链表,并且把遍历过的节点的地址存储起来,当下标P所在的地址如果在存储区中也存在,则说明链表有环,此方法的时间复杂度是O(n^2),空间复杂的是O(n),具体的程序实现在本篇中不赘述,接下来我们要讲的是一种更加简便的方法
我们需要建立两个下标分别为P,Q,他们负责从head开始遍历链表,不过Q将两个两个来遍历,P则一个一个来遍历
以此类推……那么我们只需要时刻比较P和Q的地址,如果他们相等那就说明了此链表有环,最终的情况如下:
让我们一起来看看在C++中的具体代码实现:
//判断列表是否有环
bool List::circle() {
Node* p = head;
Node* q = head;
while (q != NULL && q->next != NULL) {
p = p->next;
q = q->next->next;
if (p == q) {
return true;
}
}
return false;
}
在这段程序中,因为没有新建空间所以空间复杂的为O(1),时间复杂度也比原始方法低了
————————————黄金分割线————————————
判断完链表有环之后,我们可以进一步求出循环链表的环长:
在数学的追及问题中我们有公式 :距离=速度差*时间
在代码中,我们的时间则为循环的次数,速度差在上面的代码为1
所以我们可以把公式简化为:环长=循环次数
具体的C++代码如下:
//求循环链表环长
int List::length() {
if (circle() == true) {
Node* p = head;
Node* q = head;
int length = 0;
while (q != NULL && q->next != NULL) {
p = p->next;
q = q->next->next;
length++;
if (p == q) {
return length;
}
}
}
else {
return 0;
}
}
————————————黄金分割线————————————
借此,我们可以进一步求出入环点的位置:
假设链表head到链表入环点的距离为D,q比p多走了n圈
入环点到首次相遇点的距离记作S1,首次相遇点再回到入环点的距离为S2
Sp=D+S1;
Sq=D+S1+n(S1+S2);
又因为q每次走2步,p每次只走1步,
所以我们又有2Sp=Sq;
经过整理 D=(n-1)*(S1+S2)+S2;
那链表从head到入环点的距离相当于从首次相遇的绕多n-1圈回到入环点的距离
这样我们把指针q放回head处,并让p和q一个一个遍历数组,则当他们再次相遇的位置就是入环点
我们一起来看具体的C++代码:
int List::*pointfind() {
//将指针p和q放至入环点
Node* p = head->next;
Node* q = head->next->next;
while (q != NULL && q->next != NULL && p != q) {
p = p->next;
q = q->next->next;
}
//指针q回到head
q = head;
while (q != p) {
q = q->next;
p = p->next;
}
//p和q在入环点相遇,输出p
return p;
}
以上就是关于***判断链表有环&求循环列表环长&求循环列表入环点***的相关内容了,限于编者水平有限,内容有不妥处敬请斧正