思路:
先判断链表是否有环,有环的话取得环的长度,使用两个指针,一个先走环的长度步,然后再一起走,相交点即为环的起始节点
分析:
* 1、两个指针遍历链表,一个一次走一步,一个一次两步,能相交到一个点,则证明有环,相交点必在环上
* 2、根据相交点,遍历一圈,再次到达原节点,即取得环的长度
* 3、取得环的长度len后,一个指针先走len步,一个在一起走,第一次到达相同的节点即为环的起始节点
代码:
package com.datastructure.link;
/*
* 数据结构-单链表之获取链表环的第一个节点
*/
public class LinkedListGetCircleNode
{
/*
* 元素的基本定义
*/
static class Node{
/*
* value
*/
private int value;
/*
* 链表的下一个元素
*/
private Node next;
public Node (int value) {
this.value = value;
}
// 链表有环时 不能使用这种打印方式
@Override
public String toString() {
if (this.next == null) {
return String.valueOf(this.value);
}
return this.value + "->" + this.next.toString();
}
}
/*
* 获取单链表中环的起始节点
*
* 如:
* node0->node1->node5->node6->ode1 需要取出node1
* node0->node1->node5->node6->node0 需要取出node0
*
*
* 思路:先判断链表是否有环,有环的话取得环的长度,使用两个指针,一个先走环的长度步,然后再一起走,相交点即为环的起始节点
*
* 分析:
* 1、两个指针遍历链表,一个一次走一步,一个一次两步,能相交到一个点,则证明有环,相交点必在环上
* 2、根据相交点,遍历一圈,再次到达原节点,即取得环的长度
* 3、取得环的长度len后,一个指针先走len步,一个在一起走,第一次到达相同的节点即为环的起始节点
*
*/
/**
* 获取链表的一个环内节点,无环则返回null
*/
public static Node getCircleNodeInLink(Node node) {
if(node == null) {
return null;
}
Node first = node;
Node second = node;
// 两个指针,一个一次走一步,一个一次走两步,会相遇,说明有环
while (second != null && second.next != null && first != null) {
first = first.next;
second = second.next.next;
// 先赋值first second,在判断,避免循环刚开始即返回
if (first == second) {
return first;
}
}
return null;
}
/*
* 获取环的长度
*/
public static int getCircleLen(Node node) {
if (node == null) {
throw new RuntimeException("link is empty");
}
// 获取一个环内节点
Node oneCircleNode = getCircleNodeInLink(node);
// 以环内节点为起始,再次遍历到该节点,说明到达了一周
if (oneCircleNode != null) {
Node curNode = oneCircleNode.next;
int count = 1;
while (curNode != null) {
if (curNode == oneCircleNode) {
return count;
}
count++;
curNode = curNode.next;
}
}
return 0;
}
// 获取第一个环内节点
public static Node getFirstNodeInCircle(Node node) {
if (node == null) {
return null;
}
// 暂存环的头结点
Node fast = node;
Node low = node;
// 获取环长度
int circleLen = getCircleLen(node);
if (circleLen <= 0) {
return null;
}
for (int i = 0; i < circleLen; i++) {
fast = fast.next;
}
while (fast != null && low != null) {
if (fast == low) {
return fast;
}
fast = fast.next;
low = low.next;
}
return null;
}
public static void main(String args[]) {
Node noCircle = createTestLinkedList(5, null);
// 链表是否有环
System.out.println("link has circle? " + getCircleNodeInLink(noCircle));
// 环的长度
System.out.println("link circle len? " + getCircleLen(noCircle));
// 环的起始节点
System.out.println("link first circle node? " + getFirstNodeInCircle(noCircle));
Node node1 = new Node(11);
Node node2 = new Node(12);
node1.next = node2;
Node node3 = new Node(13);
node2.next = node3;
Node node4 = new Node(14);
node3.next = node4;
node4.next = node1;
Node circle = createTestLinkedList(5, node1);
// 链表是否有环
System.out.println("link has circle? " + getCircleNodeInLink(circle).value);
// 环的长度
System.out.println("link circle len? " + getCircleLen(circle));
// 环的起始节点
System.out.println("link first circle node value? " + getFirstNodeInCircle(circle).value);
}
private static Node createTestLinkedList(int n, Node addNode) {
Node head = new Node(0);
Node curNode = head;
for (int i = 1; i < n; i++) {
curNode.next = new Node(i);
curNode = curNode.next;
}
curNode.next = addNode;
return head;
}
}
参考:
链表题目Java实现:https://www.cnblogs.com/qianguyihao/p/4782595.html