链表成环问题
问题概述:
判断一个单链表是否有环
问题本质:
追及问题
package algorithm;
import java.util.Iterator;
public class LinkList<T> implements Iterable<T>{
//记录链表头节点
private Node head;
//记录链表长度
private int len;
//创建一个内部类,存储链表结点
private class Node{
private T element;
private Node next;
public Node(T element, Node next) {
this.element = element;
this.next = next;
}
}
public LinkList(){
head = new Node(null,null);
len = 0;
}
//使用尾插法插入元素
public void insert(T element){
Node n = head;
while(n.next!=null){
n = n.next;
}
//创建新结点
Node Newnode = new Node(element, null);
n.next = Newnode;
len++;
}
@Override
public Iterator<T> iterator() {
return new LIterator();
}
private class LIterator implements Iterator<T>{
private Node n;
public LIterator(){
this.n = head;
}
@Override
public boolean hasNext() {
return n.next!=null;
}
@Override
public T next() {
n = n.next;
return n.element;
}
}
public static void main(String[] args) {
LinkList<String> list = new LinkList<>();
list.insert("1");
list.insert("2");
list.insert("3");
list.insert("4");
list.insert("5");
//构建一个简易环
list.Cycle();
System.out.println();
//普通方法
System.out.println(list.searchlow());
//巧妙方法
System.out.println(list.search());
}
//从尾巴构建一个环(仅使用本例)
public void Cycle(){
Node n = head;
Node test = head;
while (n.next!=null){
n = n.next;
}
n.next = test.next.next;
}
//寻找方法-low
//思想:遇到一个结点,就将其之前的所有节点与该结点比较,如果两结点相同,则证明有环,否则证明无环
//使用两个标记位确定两个结点始终是一前一后,即n始终是next的遍历指针而不会和next相同,导致无环误判
//时间复杂度o(n2),空间复杂度o(n)
public Boolean searchlow() {
//获取头节点
Node n = head;
//获取头节点的next节点
Node next = head.next;
int nlen = 0;
int nextlen = 1;
while (next.next!=null){
while (nlen < nextlen){
if(n == next){
return true;
}
n = n.next;
nlen++;
}
next = next.next;
nextlen++;
nlen = 0;
n = head;
}
return false;
}
//让一个指针每次移动两个结点,另一个指针移动一个结点。
//思想:如果有环,就像运动员跑步,跑的快的总会追上跑的慢的
//时间复杂度o(n),空间复杂度o(1)
public Boolean search(){
Node n = head;
Node next = head;
while (next!=null && next.next != null){
n = n.next;
next = next.next.next;
if(n == next){
return true;
}
}
return false;
}
}
问题扩展:
- 相遇点位置
- 环的大小