1. 程式人生 > >記一道位元組跳動的演算法面試題

記一道位元組跳動的演算法面試題

前幾天有個朋友去面試位元組跳動,面試官問了他一道連結串列相關的演算法題,不過他一時之間沒做出來,就來問了我一下,感覺這道題還不錯,拿來講一講。

題目

這其實是一道變形的連結串列反轉題,大致描述如下

給定一個單鏈表的頭節點 head,實現一個調整單鏈表的函式,使得每K個節點之間為一組進行逆序,並且從連結串列的尾部開始組起,頭部剩餘節點數量不夠一組的不需要逆序。(不能使用佇列或者棧作為輔助)

例如:
連結串列:1->2->3->4->5->6->7->8->null, K = 3。那麼 6->7->8,3->4->5,1->2各位一組。調整後:1->2->5->4->3->8->7->6->null。其中 1,2不調整,因為不夠一組。

解答

這道題的難點在於,是從連結串列的尾部開始組起的,而不是從連結串列的頭部,如果是頭部的話,那我們還是比較容易做的,因為你可以遍歷連結串列,每遍歷 k 個就拆分為一組來逆序。但是從尾部的話就不一樣了,因為是單鏈表,不能往後遍歷組起。不過這道題肯定是用遞迴比較好做,對遞迴不大懂的建議看我之前寫的一篇文章為什麼你學不會遞迴?告別遞迴,談談我的一些經驗,這篇文章寫了關於遞迴的一些套路。

先做一道類似的反轉題

在做這道題之前,我們不仿先來看看如果從頭部開始組起的話,應該怎麼做呢?例如:連結串列:1->2->3->4->5->6->7->8->null, K = 3。調整後:3->2->1->6->5->4->7->8->null。其中 7,8不調整,因為不夠一組。

對於這道題,如果你不知道怎麼逆序一個單鏈表,那麼可以看一下我之前寫的如何優雅著反轉單鏈表

這道題我們可以用遞迴來實現,假設方法reverseKNode()的功能是將單鏈表的每K個節點之間逆序(從頭部開始組起的哦);reverse()方法的功能是將一個單鏈表逆序。

那麼對於下面的這個單鏈表,其中 K = 3。


我們把前K個節點與後面的節點分割出來:


temp指向的剩餘的連結串列,可以說是原問題的一個子問題。我們可以呼叫reverseKNode()方法將temp指向的連結串列每K個節點之間進行逆序。再呼叫reverse()方法把head指向的那3個節點進行逆序,結果如下:

再次宣告,如果對這個遞迴看不大懂的,建議看下我那篇遞迴的文章

接著,我們只需要把這兩部分給連線起來就可以了。最後的結果如下:

程式碼如下:

    //k個為一組逆序
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode temp = head;
        for (int i = 1; i < k && temp != null; i++) {
            temp = temp.next;
        }
        //判斷節點的數量是否能夠湊成一組
        if(temp == null)
            return head;

        ListNode t2 = temp.next;
        temp.next = null;
        //把當前的組進行逆序
        ListNode newHead = reverseList(head);
        //把之後的節點進行分組逆序
        ListNode newTemp = reverseKGroup(t2, k);
        // 把兩部分連線起來
        head.next = newTemp;
        
        return newHead;
    }
    
    //逆序單鏈表
    private static ListNode reverseList(ListNode head) {
        if(head == null || head.next == null)
            return head;
        ListNode result = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return result;
    }

回到本題

這兩道題可以說是及其相似的了,只是一道從頭部開始組起,這道從頭部開始組起的,也是 leetcode 的第 25 題。而面試的時候,經常會進行變形,例如這道位元組跳動的題,它變成從尾部開始組起,可能你一時之間就不知道該怎麼弄了。當然,可能有人一下子就反應出來,把他秒殺了。

其實這道題很好做滴,你只需要先把單鏈表進行一次逆序,逆序之後就能轉化為從頭部開始組起了,然後按照我上面的解法,處理完之後,把結果再次逆序即搞定。兩次逆序相當於沒逆序。

例如對於連結串列(其中 K = 3)


我們把它從尾部開始組起,每 K 個節點為一組進行逆序。步驟如下

1、先進行逆序


逆序之後就可以把問題轉化為從頭部開始組起,每 K 個節點為一組進行逆序。

2、處理後的結果如下

3、接著在把結果逆序一次,結果如下

程式碼如下

public ListNode solve(ListNode head, int k) {
    // 呼叫逆序函式
    head = reverse(head);
    // 呼叫每 k 個為一組的逆序函式(從頭部開始組起)
    head = reverseKGroup(head, k);
    // 在逆序一次
    head = reverse(head);
    return head;
    
}

類似於這種需要先進行逆序的還要兩個連結串列相加,這道題位元組跳動的筆試題也有出過,如下圖的第二題

這道題就需要先把兩個連結串列逆序,再節點間相加,最後在合併了。

總結

關於連結串列的演算法題,在面試的時候聽說是挺常考的,大家可以多注意注意,遇到不錯的連結串列演算法題,也歡迎扔給我勒。

如果你覺得這篇內容對你挺有啟發,為了讓更多的人看到這篇文章:不妨

1、點贊,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓 -_-)

2、關注我和專欄,讓我們成為長期關係

3、關注公眾號「苦逼的碼農」,主要寫演算法、計算機基礎之類的文章,裡面已有100多篇原創文章


大部分的資料結構與演算法文章被各種公眾號轉載相信一定能讓你有所收穫

我也分享了很多視訊、書籍的資源,以及開發工具,歡迎各位的關注我的公眾號:苦逼的碼農,第一時間閱讀我的文章