力扣142:環形鏈表II

環形鏈表II:給定一個鏈表,返回鏈表開始入環的第一個節點。 如果鏈表無環,則返回 null。
爲了表示給定鏈表中的環,我們使用整數 pos 來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。 如果 pos 是 -1,則在該鏈表中沒有環。
說明:不允許修改給定的鏈表。

解答

方法1:

思路:
我們可以使用一個set集合來存儲元素,當如果set中有重複值出現是代表有環且爲環的入口
算法:
首先,我們分配一個 Set 去保存所有的列表節點。我們逐一遍歷列表,檢查當前節點是否出現過,如果節點已經出現過,那麼一定形成了環且它是環的入口。否則如果有其他點是環的入口,我們應該先訪問到其他節點而不是這個節點。其他情況,沒有成環則直接返回 null 。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        Set<ListNode> set = new HashSet<>();
        while(head != null){
            if(set.contains(head)){
                return head;
            }else{
                set.add(head);
                head=head.next;
            }
        }
        return null;
    }
}
  • 時間複雜度O(n)
    不管是成環還是不成環的輸入,算法肯定都只會訪問每個節點一次。對於非成環列表這是顯而易見的,因爲第 nnn 個節點指向 null ,這會讓循環退出。對於循環列表, if 條件滿足時會導致函數的退出,因爲它指向了某個已經訪問過的節點。兩種情況下,訪問的節點數最多都是 nnn 個,所以運行時間跟節點數目成線性關係。
  • 空間複雜度O(n)
    不管成環或者不成歡的輸入,我們都需要將每個節點插入 Set 中一次。兩者唯一的區別是最後訪問的節點後是 null 還是一個已經訪問過的節點。因此,由於 Set 包含 nnn 個不同的節點,所需空間與節點數目也是線性關係的。

方法2:Floyd 算法

思路:
當然一個跑得快的人和一個跑得慢的人在一個圓形的賽道上賽跑,會發生什麼?在某一個時刻,跑得快的人一定會從後面趕上跑得慢的人。
算法
Floyd 的算法被劃分成兩個不同的階段 。(1)找出列表中是否有環,如果沒有環,可以直接返回 null 並退出。(2)用相遇節點來找到環的入口。

階段1:
這裏我們初始化兩個指針 - 快指針和慢指針。我們每次移動慢指針一步、快指針兩步,直到快指針無法繼續往前移動。如果在某次移動後,快慢指針指向了同一個節點,我們就返回它。否則,我們繼續,直到 while 循環終止且沒有返回任何節點,這種情況說明沒有成環,我們返回 null 。

階段2
給定階段 1 找到的相遇點,階段 2 將找到環的入口。首先我們初始化額外的兩個指針: ptr1 ,指向鏈表的頭, ptr2 指向相遇點。然後,我們每次將它們往前移動一步,直到它們相遇,它們相遇的點就是環的入口,返回這個節點。

public class Solution {
    private ListNode getIntersect(ListNode head) {
        ListNode tortoise = head;
        ListNode hare = head;

        // A fast pointer will either loop around a cycle and meet the slow
        // pointer or reach the `null` at the end of a non-cyclic list.
        while (hare != null && hare.next != null) {
            tortoise = tortoise.next;
            hare = hare.next.next;
            if (tortoise == hare) {
                return tortoise;
            }
        }

        return null;
}
    public ListNode detectCycle(ListNode head) {
        if (head == null) {
            return null;
        }
        // If there is a cycle, the fast/slow pointers will intersect at some
        // node. Otherwise, there is no cycle, so we cannot find an e***ance to
        // a cycle.
        ListNode intersect = getIntersect(head);
        if (intersect == null) {
            return null;
        }
        // To find the e***ance to the cycle, we have two pointers traverse at
        // the same speed -- one from the front of the list, and the other from
        // the point of intersection.
        ListNode ptr1 = head;
        ListNode ptr2 = intersect;
        while (ptr1 != ptr2) {
            ptr1 = ptr1.next;
            ptr2 = ptr2.next;
        }
        return ptr1;
    }
}
  • 時間複雜度O(n)
  • 空間複雜度O(1)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章