单链表之判断链表中是否有环

想想这个问题是来湖大复试的时候老师问的,到现在已经有一年多了,今天才来整理,想想挺惭愧的。

首先链表节点声明如下:

class Node{
	int val;
	Node next;
	public Node(int val){
		this.val = val;
	}
}

然后我们再来利用尾插法造一个链表:

	public void addNodeFail(int val){
		Node node = new Node(val);
		if(head==null){
			head = node;
			return;
		}
		Node temp = head;
		while(temp.next!=null){
			temp = temp.next;
		}
		temp.next = node;
	}

那么,什么是链表中有环呢?

这样的链表就是有环的,试想,我们搞一个指针一直遍历,是永远不会结束的。

我们先来造个环吧。

	//自己造个环
	public void CreateCircle(Node head){
		Node temp = head;
		int i=1;
		Node circle = null;
		while(temp!=null){
			if(i==3){
				circle = temp; //保存第3个节点
			}
			if(i==6){
				temp.next = circle; //第6个节点直接就指向刚刚保存的第3个节点上
				break;
			}
			temp = temp.next;
			i++;
		}
	}

这个时候如果调用print()的话,就会一直输出,因为它一直在环内打转转。

 

解决方法:用两个指针,一个指针一次走一步,另一个指针一次走两步,如果存在环,则这两个指针会在环内相遇

	//判断是否有环
	public Boolean hasCircle(Node head){
		Node slow = head;
		Node fast = head;
		while(fast!=null&&fast.next!=null){
			if(slow==fast) return true; //相遇,存在环
			slow = slow.next;
			fast = fast.next.next;
		}
		return false;
	}

那么拓展问题来了:   (参考来自:http://www.cnblogs.com/ghimtim/p/4882916.html

1.找环的入口点

假设单链表的总长度为L,头结点到环入口的距离为a,环入口到快慢指针相遇的结点距离为x,环的长度为r,慢指针总共走了s步,则快指针走了2s步。另外,快指针要追上慢指针的话快指针至少要在环里面转了一圈多(假设转了n圈加x的距离),得到以下关系:    
    s = a + x
    2s = a + nr + x
    =>a + x = nr
    =>a = nr - x

那么,若在头结点和相遇结点分别设一指针,单步前进,第一个指针走过a步,第二个指针走过(nr-x)步,因为a=nr-x,所以它们一定环入口结点相遇。

	//找出环的入口
	public Node searchEntranceNode(Node head){
		Node slow = head;
		Node fast = head;
		while(fast!=null&&fast.next!=null){
			if(slow==fast)  break;
			slow = slow.next;
			fast = fast.next.next;
		}
		if(fast==null||fast.next==null)  return null; //当没有环时返回null
		slow = head;
		while(slow!=fast){
			slow = slow.next;
			fast = fast.next;
		}
		return slow; //有环时就返回环入口节点
	}

2.求环的长度

我们都知道环入口点了,就直接在环入口点设置一个指针,遍历下去,等到下次指针来到环入口点时,看看走了几个节点就是环的长度了。

	//求环的长度
	public int circleLength(Node head){
		Node node = searchEntranceNode(head); //找出环入口结点
		if(node==null)  return 0;  //不存在环的话就直接返回0了
		Node temp = node;
		int len = 1;
		while(node!=temp){
			len++;
			temp = temp.next;
		}
		return len;
	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值