鍊錶是我們資料結構面試中比較容易出錯的問題,所以很多面試官總喜歡在這上面下功夫,為了避免出錯,我們最好先進行全面的分析。在實際軟體開發周期中,設計的時間通常不會比編碼的時間短,在面試的時候我們不要著急於寫**,而是一開始仔細分析和設計,這將給面試官留下乙個很好的印象。
與其很快寫出一段千瘡百孔的**,不容仔細分析後再寫出健壯性無敵的程式。
面試題:輸入乙個單鏈表的頭結點,返回它的中間元素。為了方便,元素值用整型表示。當應聘者看到這道題的時候,內心一陣狂喜,怎麼給自己遇到了這麼簡單的題。拿起筆就開始寫,先遍歷整個鍊錶,拿到鍊錶的長度 len,再次遍歷鍊錶,位於 len/2 的元素就是鍊錶的中間元素。
所以這個題最重要的點就是拿到鍊錶的長度 len。而拿到這個 len 也比較簡單,只需要遍歷前設定乙個 count 值,遍歷的時候 count++ ,第一次遍歷結束,就拿到單鏈表的長度 len 了。
於是我們很快寫出了這樣的**:
public class test15 } private static int getthemid(linknode head) for (int i = 0; i < count / 2; i++) return node.data; } public static void main(string args) }
面試官看到這個**的時候,他告訴我們上面**迴圈了兩次,但是他期待的只有一次。
於是我們絞盡腦汁,突然想到了網上介紹過的乙個概念:快慢指標法。
假設我們設定兩個變數 slow、fast 起始都指向單鏈表的頭結點當中,然後依次向後面移動,fast 的移動速度是 slow 的 2 倍。這樣當 fast 指向末尾節點的時候,slow 就正好在正中間了。
想清楚這個思路後,我們很快就能寫出如下**:
public class test15 } private static int getthemid(linknode head) return slow.data; } public static void main(string args) }
快慢指標法舉一反三
快慢指標法 確實在鍊錶類面試題中特別好用,我們不妨在這裡舉一反三,對原題稍微修改一下,其實也可以實現。
面試題:給定乙個單鏈表的頭結點,判斷這個鍊錶是否是迴圈鍊錶。和前面的問題一樣,我們只需要定義兩個變數 slow,fast,同時從鍊錶的頭結點出發,fast 每次走鍊錶,而 slow 每次只走一步。如果走得快的指標追上了走得慢的指標,那麼鍊錶就是環形(迴圈)鍊錶。如果走得快的指標走到了鍊錶的末尾(fast.next 指向 null)都沒有追上走得慢的指標,那麼鍊錶就不是環形鍊錶。
有了這樣的思路,實現**那還不是分分鐘的事兒。
public class test15 } private static boolean isringlink(linknode head) fast = fast.next.next; slow = slow.next; } return false; } public static void main(string args) }
確實有意思,快慢指標法 再一次利用它的優勢巧妙解決了我們的問題。
快慢指標法的延展
我們上面講解的「快慢指標法」均是乙個變數走 1 步,乙個變數走 n 步。我們其實還可以拓展它。這個「快慢」並不是說一定要同時遍歷。
比如《劍指offer》中的第 15 道面試題,就運用到了「快慢指標法」的延展。
面試題:輸入乙個單鏈表的頭結點,輸出該鍊錶中倒數第 k 個節點的值。初一看這個似乎並不像我們前面學習到的「快慢指標法」的考察。所以大多數人就迷糊了,進入到常規化思考。依然還是設定乙個整型變數 count,然後每次迴圈的時候 count++,拿到鍊錶的長度 n。那麼倒數第 k 個節點也就是順數第 n-k+1 個結點。所以我們只需要在拿到長度 n 後再進行一次 n-k+1 次迴圈就可以拿到這個倒數第 k 個節點的值了。
但面試官顯然不會太滿意這個臃腫的解法,他依然希望我們一次迴圈就能搞定這個事。
為了實現只遍歷一次鍊錶就能找到倒數第 k 個結點,我們依然可以定義兩個遍歷 slow 和 fast。我們讓 fast 變數先往前遍歷 k-1 步,slow 保持不動。從第 k 步開始,slow 變數也跟著 fast 變數從鍊錶的頭結點開始遍歷。由於兩個變數指向的結點距離始終保持在 k-1,那麼當 fast 變數到達鍊錶的尾結點的時候,slow 變數指向的結點正好是我們所需要的倒數第 k 個結點。
我們依然可以在心中預設一遍**:
假設輸入的鍊錶是:1->2->3->4->5;
現在我們要求倒數第三個結點的值,即順數第 3 個結點,它的值為 3;
定義兩個變數 slow、fast,它們均指向結點 1;
先讓 fast 向前走 k-1 即 2 步,這時候 fast 指向了第 3 個結點,它的值是 3;
現在 fast 和 slow 同步向右移動;
fast 再經過了 2 步到達了鍊錶尾結點;fast 正好指向了第 3 個結點,這顯然是符合我們的猜想的。
在心中默走了一遍**後,我們顯然很容易寫出下面的**。
public class test15 } private static int getspecifiednodereverse(linknode head, int k) // 先讓 fast 先走 k-1 步 for (int i = 0; i < k - 1; i++) fast = fast.next; } while (fast.next != null) return slow.data; } public static void main(string args) }
總結
鍊錶類面試題,真是可以玩出五花八門,當我們用乙個變數遍歷鍊錶不能解決問題的時候,我們可以嘗試用兩個變數來遍歷鍊錶,可以讓其中乙個變數遍歷的速度快一些,比如一次走兩步,或者是走若干步。我們在遇到這類面試的時候,千萬不要自亂陣腳,學會理性分析問題。
原本是想給我的小夥伴說再見了,但唯恐大家還沒學到真本事,所以在這裡再留乙個拓展題。
面試題:給定乙個單鏈表的頭結點,刪除倒數第 k 個結點。哈哈,和上面的題目僅僅只是把獲得它的值變成了刪除,不少小夥伴肯定都偷著樂了,但南塵還是先提醒大家,不要太得意忘形喲~
好啦,咱們明天再見啦~
環形鍊錶(快慢指標法)
給定乙個鍊錶,判斷鍊錶中是否有環。如果鍊錶中有某個節點,可以通過連續跟蹤 next 指標再次到達,則鍊錶中存在環。為了表示給定鍊錶中的環,我們使用整數 pos 來表示鍊錶尾連線到鍊錶中的位置 索引從 0 開始 如果 pos 是 1,則在該鍊錶中沒有環。注意 pos 不作為引數進行傳遞,僅僅是為了標識...
鍊錶 尋找中間結點(快慢指標法)
一般方法 鍊錶這種無法隨機訪問的資料結構,要定位到中間結點的方法通常是先把整個鍊錶遍歷一遍,得到整個鍊錶的長度,再除以2,得到中間結點的位置,接著根據這個位置從頭結點開始找到這個結點,時間複雜度為o 1.5n 具體 如下 definition for singly linked list.struc...
演算法練習 回文鍊錶 快慢指標 鍊錶翻轉
題目 編寫乙個函式,檢查輸入的鍊錶是否是回文的。示例 輸入 1 2 輸出 false個人思路 回文鍊錶即字元左右對稱,但是鍊錶是單向的,這時我們就要讓鍊錶後半部分進行翻轉,首先遍歷鍊錶獲得長度,然後讓快指標走到鍊錶的後半部分開頭,然後開始進行後半部分的鍊錶翻轉,翻轉結束後快指標走到鍊錶的結尾,這時讓...