面试题题目如下:
1. 判断链表是否带环
2. 求带环链表中环的入口点
3. 求带环链表中环的长度
对题目1和2的分析:
1. 判断链表是否带环
分析:定义两个指针:快指针和慢指针,快指针一次走两步,慢指针一次走一步;
如果链表带环,那么快慢指针会在环中相遇【快指针等于满指针】
如果快指针走到头时,快慢指针没有相等的时候,那么说明链表不带环
2. 如果链表带环,如何找环的入口点
分析:如果一个链表有环,当快指针和慢指针相遇时,慢指针一定还没有遍历完链表,而此时快指针已经在环内循环走了n(n>=1)圈了。
假设此时
慢指针走了s步,快指针走了2s步,
r为快指针在环内转了一圈的步数,
a为从链表头到入口点的步数,
b为从入口点到相遇点的步数,
c为从相遇点再走c步到达入口点,
L为整个链表的长度。
那么:
慢指针走的步数:
s = a + b
快指针走的步数:
2s = s + n*r 即:s = n*r
链表的长度:
L = a + b + c = a+r
由上可知:
a + b = n*r = (n - 1)*r + r
而r = L - a
所以:
a + b = (n - 1)*r + L - a
a = (n - 1)*r + L - a - b
而L - a - b = c
所以:
a = (n -1)*r +c
综上可得:从链表头到环入口点等于(n - 1)循环内环 + 相遇点到环入口点,于是在链表头和相遇点分别设置一个指针,同时出发,每次各走一步,它们必定会相遇,且第一次相遇的点就是环入口点。
具体实现如下:
先定义节点类-Node类
public class Node {
private int data;
private Node next;
Node(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
具体实现及测试代码如下:
public class LinkedList {
//判断链表是否带环,若不带环,返回null
public Node hasCircle(Node first) {
if (first == null) {
return null;
}
Node slow = first;
Node fast = first;
while (fast != null && fast.getNext() != null) {
slow = slow.getNext();
fast = fast.getNext().getNext();
if (slow == fast) {
return slow;
}
}
return null;
}
//若链表带环,求环的入口点
public Node getCircleEntry(Node first) {
if (hasCircle(first) == null) {
return null;
}
Node meetNode = hasCircle(first);
Node tail = first;
while (tail != meetNode) {
tail = tail.getNext();
meetNode = meetNode.getNext();
}
return tail;
}
//若链表带环,求环的长度
public int getCircleLength(Node first) {
if (getCircleEntry(first) == null) {
return 0;
}
int len = 1;//带环的链表,环的长度至少是1
Node entryNode = getCircleEntry(first);
Node node = entryNode;
while (node.getNext() != entryNode) {
node = node.getNext();
len++;
}
return len;
}
public static void main(String[] args) {
LinkedList list = new LinkedList();
Node n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
Node n4 = new Node(4);
Node n5 = new Node(5);
Node n6 = new Node(6);
n1.setNext(n2);
n2.setNext(n3);
n3.setNext(n4);
n4.setNext(n5);
n5.setNext(n6);
n6.setNext(n3);
//判断链表是否带环
System.out.println(list.hasCircle(n1));
//求带环链表中环的入口点
System.out.println(list.getCircleEntry(n1).getData());
//求带环链表中环的长度
System.out.println(list.getCircleLength(n1));
}
}