LeetCode進階-實戰之快慢指針(阿里面試題)

閒聊

快慢指針的使用常常會出如今各大公司的面試題中,雖然出題形式千差萬別但本質思想卻異曲同工(看下文說明)。好比近期github開源的面試題項目,其中就有一道比較基礎的題目考察對快慢指針的理解。在數據結構與算法的學習過程當中學會觸類旁通很關鍵。java

阿里面試題-給定一個鏈表,刪除鏈表的倒數第N個節點,而且返回鏈表的頭結點

示例: 給定一個鏈表: 1->2->3->4->5, 和 n = 2. 當刪除了倒數第二個節點後,鏈表變爲 1->2->3->5. 說明: 給定的 n 保證是有效的。 要求: 只容許對鏈表進行一次遍歷。node

出題人:阿里巴巴出題專家:屹平/阿里雲視頻雲邊緣計算高級技術專家git

參考答案

咱們可使用兩個指針而不是一個指針。第一個指針從列表的開頭向前移動 n+1 步,而第二個指針將從列表的開頭出發。如今,這兩個指針被 n 個結點分開。咱們經過同時移動兩個指針向前來保持這個恆定的間隔,直到第一個指針到達最後一個結點。此時第二個指針將指向從最後一個結點數起的第 n 個結點。咱們從新連接第二個指針所引用的結點的 next 指針指向該結點的下下個結點。github

參考代碼

public ListNode removeNthFromEnd(ListNode head, int n) {
    ListNode dummy = new ListNode(0);
    dummy.next = head;
    ListNode first = dummy;
    ListNode second = dummy;
    // Advances first pointer so that the gap between first
    and second is n nodes apart for (int i = 1; i <= n + 1; i++) {
        first = first.next;
    }
    // Move first to the end, maintaining the gap
    while (first != null) {
        first = first.next;
        second = second.next;
    }
    second.next = second.next.next;
    return dummy.next;
}
複製代碼

複雜度分析

  • 時間複雜度:O(L),該算法對含有 L 個結點的列表進行了一次遍歷。所以時間複雜度爲 O(L)。面試

  • 空間複雜度:O(1),咱們只用了常量級的額外空間。算法

延伸

快慢指針類型的題目其實在leetcode上很常見,基本上有過快慢指針相關刷題經驗的話在碰見本題的時候都可以秒解。隨便從leetcode上摘一題做具體分析,好比876. Middle of the Linked List(鏈表的中間節點)數組

876. Middle of the Linked List

Given a non-empty, singly linked list with head node head, return a middle node of linked list.bash

If there are two middle nodes, return the second middle node.數據結構

Example 1:學習

Input: [1,2,3,4,5] Output: Node 3 from this list (Serialization: [3,4,5]) The returned node has value 3. (The judge's serialization of this node is [3,4,5]). Note that we returned a ListNode object ans, such that: ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, and ans.next.next.next = NULL. Example 2:

Input: [1,2,3,4,5,6] Output: Node 4 from this list (Serialization: [4,5,6]) Since the list has two middle nodes with values 3 and 4, we return the second one.

Note:

The number of nodes in the given list will be between 1 and 100.

876. 鏈表的中間結點

一個帶有頭結點head的非空單鏈表,返回鏈表的中間結點。 若該鏈表有兩個中間結點,則返回第二個中間結點。

例1:

輸入:[1,2,3,4,5] 輸出:此列表中的結點爲3 (序列化形式:[3,4,5]) 返回的結點爲3。 (檢測系統序列化遍歷該節點 [3,4,5]) 注意,咱們返回了的ListNode類型的數組ans,知足: ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.(意思是會最後會對結果middle以及其後的節點做校驗)

例2:

輸入:[1,2,3,4,5,6] 輸出:此列表中的結點 4 (檢測系統序列化遍歷該節點:[4,5,6]) 由於該列表有兩個中間結點,值分別爲3和4,最終返回第二個結點。

提示:

鏈表結點數介於1到100之間。

題意分析

本題屬於典型的快慢指針查找題型,關於怎麼確認鏈表相關的題型是否屬於快慢指針題型,能夠簡單的根據題意判斷:凡事須要查找的結果節點位置和鏈表位置相對位置明確,有屬於鏈表節點查找問題,都可以條件反射般聯想到快慢指針,就像早晨聽見雞打鳴就知道天快亮了同樣。

思路設計

快慢指針思想,快指針和慢指針。每次循環的時候快指針後移兩次,慢指針後移一次,達到邊界時候中止循環,最終慢指針的位置必然爲中間節點位置,由於移動次數快指針始終是慢指針的2倍。

##僞代碼

一、聲明兩個指針,慢指針和快指針;
     二、while循環,循環條件爲指針最終爲null,即到達邊界;
       i.慢指針循環中每次移動一次;
       ii.快指針循環中每次移動兩次;
     三、返回循環結束後的慢指針節點;  
     
複製代碼

編碼實踐

public ListNode middleNode(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
複製代碼

複雜度分析

  • 時間複雜度:O(n),該算法對含有n個結點的列表進行了一次遍歷,實際遍歷次數爲n/2。

  • 空間複雜度:O(1),咱們只用了常量級的額外空間。

結語

對比阿里面試題以及leetcode876的題解分析能夠驗證出快慢指針的思路都是利用兩個節點指針,在循環中同時移動快指針和慢指針,快指針與慢指針的位置根據需求設計,利用快指針在循環遍歷過程當中提早到達邊界的這一特性,慢指針的最後到達位置即最終結果。經過對比進一步說明了要進國內大廠,數據結構與算法的重要性,刷leetcode的重要性!最後,若是以爲本文對你有所幫助或啓發那就來個贊吧0.0~~

關注訂閱號 獲取更多幹貨~
相關文章
相關標籤/搜索