一、提纲
0、单向链表的翻转
1、判断链表是否有环
2、寻找环的入口点
3、计算环的节点数
4、计算(有环)链表的节点数
5、找出环中距任意一点最远的节点
6、判断两个无环链表是否相交
7、寻找两个链表的相交的节点
二、核心算法
以上问题的核心算法都是利用“快慢指针”
快指针:步长为2
慢指针:步长为1
快慢指针一开始都指向链表头,同时遍历链表,若链表有环,快慢指针必然会在环中相遇,而且是在慢指针未走完一圈时相遇。若存在相遇点,则说明链表有环;若不存在,则没有环。
之后的问题都是直接或间接通过相遇点求得答案。
三、核心代码
/**
* @Title: findMeetingNode
* @Description: 寻找链表中环的相遇节点(若有环,快慢指针必会在环中相遇)
* @param head 链表头
* @return 有环返回相遇节点,无环返回null
* @return Node 返回类型
* @throws
*
* 通过快慢指针判断链表是否有环
*若有环,必会在环内相遇,且是slow未走完一圈时相遇
*/
private static Node findMeetingNode(Node head){
Node fast = head;
Node slow = head;
while (fast != null && slow != null) {
if (fast.next != null) {
fast = fast.next.next;
} else {
//fast.next为null,说明无环,直接返回null
return null;
}
slow = slow.next;
if (fast == slow) {
// 若相等说明有环,返回相遇节点
return fast;
}
}
//若无环,说明fast为null
return fast;
}
四、关键代码
1、判断链表是否有环
/**
* @Title: exitLoop
* @Description: 通过findMeetingNode的返回节点判断是否有环
* 若返回节点不为空,必然有环,反之亦然
* @param head 链表头
* @return 有环返回true,否则返回false
* @return boolean 返回类型
* @throws
*/
public static boolean existLoop(Node head) {
//寻找环中相遇点
Node meet = findMeetingNode(head);
return meet != null ? true : false;
}
2、寻找环的入口点
/**
* @Title: findLoopStartNode
* @Description: 寻找环的入口点,找到则返回入口点,找不到则返回null
* @param head 链表头
* @return 入口节点或null
* @return Node 返回类型
* @throws
* 若单向链表中环,通过快慢指针找到在环中相遇的节点meet
* 令一个指针指向链表头,一个指针指向meet,步长为1往前走
* 两指针必会在环的入口点相遇
*
*/
public static Node findLoopStartNode(Node head){
//寻找环中相遇点
Node meet = findMeetingNode(head);
if (meet!=null) {
Node point1=head;
Node point2=meet;
while (point1!=point2) {
point1=point1.next;
point2=point2.next;
}
//相遇点便是入口点
return point1;
} else {
return null;
}
}
3、计算环的节点数
/**
* @Title: countLoop
* @Description: 统计环中节点个数。
* @param head 链表头
* @return 环中节点个数
* @return int 返回类型
* @throws
* 找到环中相遇节点后,令指针从相遇节点走一圈,统计走过的节点数
*
*/
public static int countLoop(Node head){
//找出环中相遇节点
Node meet = findLoopStartNode(head);
if (meet!=null) {
Node point=meet.next;
int count=1;
while (point!=meet) {
point=point.next;
count++;
}
return count;
} else {
//若节点为null则说明无环,返回0
return 0;
}
}
4、计算(有环)链表的节点数
/**
* @Title: countLinkedList
* @Description: 统计链表的节点个数(无论是否有环)
* @param head 链表头
* @return 链表的节点个数
* @return int 返回类型
* @throws
*
* 环的节点个数+链表头到环入口点节点个数=链表节点个数
*/
public static int countLinkedList(Node head){
//找出环的长度
int loop=countLoop(head);
//找出环的入口点
Node startNode = findLoopStartNode(head);
int head2start=0;
Node point=head;
//计算链表头到入口点长度
while (point!=startNode) {
point=point.next;
head2start++;
}
//环的长度+链表头到环入口点长度即为链表长度
return loop+head2start;
}
5、找出环中距任意一点最远的节点
/**
* @Title: findTheFarthestNodeInLoop
* @Description: 指定环中任意一个节点,在环中寻找离该节点最远的一点
* @param theNode 任意一节点
* @return 存在这样一个节点则返回该节点,若不存在返回null
* @return Node 返回类型
* @throws
* 环中距任一点最远的点,即是从该点出发走半圈到达的点
*
*/
public static Node findTheFarthestNodeInLoop(Node theNode){
//判断是否存在环
if (!existLoop(theNode)) {
//不存在环直接返回null
return null;
}
Node fast = theNode;
Node slow = theNode;
while (fast != theNode && fast != theNode.next) {
fast = fast.next.next;
slow = slow.next;
}
if (fast == null) {
return fast;
} else {
return slow;
}
}
6、判断两个无环链表是否相交
/**
* @Title: existIntersect
* @Description: 判断两个无环链表是否相交
* @param listA 链表A
* @param listB 链表B
* @return 相交返回true,不相交返回false
* @return boolean 返回类型
* @throws
*
* 判断两个无环链表是否相交
* 将其中一个链表(链表A)的尾指针指向链表头,判断另一个链表(链表B)是否有环,
* 此时,链表A是肯定有环的;若两链表不相交,链表B是肯定没有环的
* 若链表B有环,两个链表必然相交
*/
public static boolean isIntersect(MyLinkedList listA,MyLinkedList listB){
//将A链表的尾指向链表头
listA.getLast().setNext(listA.getHead());
//判断链表B是否有环
boolean intersection=existLoop(listB.getHead());
//将链表A还原
listA.getLast().setNext(null);
//有环说明相交,无环说明不相交
return intersection;
}
7、寻找两个链表的相交的节点
/**
* @Title: findIntersection
* @Description: 找出两链表的相交点
* @param listA 链表A
* @param listB 链表B
* @return 若有相交点则返回该节点,否则返回null
* @return Node 返回类型
* @throws
*/
public static Node findIntersection(MyLinkedList listA,MyLinkedList listB){
//将A链表的尾指向链表头
listA.getLast().setNext(listA.getHead());
//找出环的入口点,入口点既是相交点
Node Intersection = findLoopStartNode(listB.getHead());
//将链表A还原
listA.getLast().setNext(null);
return Intersection;
}
上面只给了关键代码,需要完整代码的读者可以在这里下载(由于规则限制,无法设置下载积分为0分)