這個問題是 LintCode 上面的一個算法題目( 帶環鏈表 II ),主要考察的是對於鏈表,帶環鏈表的理解。
原問題描述:
給定一個鏈表,如果鏈表中存在環,則返回到鏈表中環的起始節點,如果沒有環,返回null。
樣例:
給出 -21->10->4->5, tail connects to node index 1,返回10
分析:這個問題的思路主要分爲以下幾步:
1.判斷這個鏈表是不是帶環鏈表
2.如果是帶環鏈表,則返回環上的一個節點
3.從頭節點開始,一步一步往後挪動,每次都判斷節點是不是在環上面
判斷方法: 使得環上的節點繞環一週,如果遇到了測試節點,則說明測試節點是環上面的一個節點
如果繞完一週以後,一直沒有碰到測試節點,說明測試節點不是環上面的一個節點
解決代碼:
public class TestDetectCycle {
public ListNode detectCycle(ListNode head){
//如果存在循環體,則鏈的下一個節點是不會爲空的
if (head == null || head.next == null) return null;
//找到鏈上面的一個節點
ListNode nodeInCycle = findANodeInCycle(head, head.next);
//如果返回爲空,則說明沒有循環體
if (nodeInCycle == null) return null;
//將頭結點和環上面任意節點傳入,尋找起始點
return findStart(head, nodeInCycle);
}
/**
* 在鏈表上的循環體內找到一個節點
* @param node1 每次跨度爲 1 的節點
* @param node2 每次跨度爲2的節點
* @return 返回的是循環體內的一個節點,如果沒有循環體,則返回null
*/
private ListNode findANodeInCycle(ListNode node1, ListNode node2) {
//只有任意一個節點爲空,則說明沒有循環體,返回null
if (node1 == null || node2 == null) return null;
//說明兩個跨度不同的節點碰到了一起,那麼此時此時節點就在循環體內
if (node1 == node2) return node1;
//這是爲了防止下一行的代碼報空指針異常
//只要某一個節點的下一個節點爲空,則說明沒有循環體
if (node2.next == null) return null;
//通過移動不同的跨度,尋找循環體
return findANodeInCycle(node1.next, node2.next.next);
}
/**
* 尋找循環體的起始節點
* @param node 頭結點
* @param nodeInCycle 循環體上面的一個節點
* @return 返回其實節點
*/
private ListNode findStart(ListNode node, ListNode nodeInCycle) {
while ( ! isStartNode(node, nodeInCycle)){
//這個節點不是起始節點,則移動到下一節點,繼續驗證
node = node.next;
}
return node;
}
private boolean isStartNode(ListNode node, ListNode nodeInCycle) {
//爲了讓while循環體順利進行,一開始不能讓node2 == nodeInCycle,只要讓node2往後挪動一步就行了
ListNode node2 = nodeInCycle.next;
//while代碼段會讓node2繞着循環體跑一圈,如果能遇到node,說明node就在循環體上
//如果繞完一圈後,還是沒有遇到node,說明node不在循環體上面
while (node2 != nodeInCycle) {
if (node == node2) return true;
node2 = node2.next;
}
//因爲當尾部節點指向其自身的話,while中的循環體是不會進去的,因爲此時nodeInCycle == node2
//這時只要判斷test節點node是不是等於nodeInCycle就行了
return node == node2;
}
}
關於判斷鏈表是否有環,更具體的請參考我的另一篇博客 帶環鏈表