面試 8:快慢指針法玩轉鏈表算法面試(二)

昨天在最後給你們留了拓展題,不知道你們有沒有思考完成,其實南塵說有巨坑是嚇你們的啦,實際上也沒什麼。咱們來繼續看看昨天這個拓展題。java

面試題:給定單鏈表的頭結點,刪除單鏈表的倒數第 k 個結點。node

前面的文章見連接:面試 7:面試常見的鏈表算法捷徑(一)面試

這個題和前面的文章中增長了一個操做,除了找出來這個結點,咱們還要刪除它。刪除一個結點,想必你們一定也知道:要想操做(添加、刪除)單鏈表的某個結點,那咱們還得知道這個節點的前一個節點。因此咱們要刪除倒數第 k 個結點,就必需要找到倒數第 k+1 個結點。而後把倒數第 k+1 個元素的 next 變量 p.next 指向 p.next.next。算法

咱們找到倒數第 k 個結點的時候,先讓 fast 走了 k-1 步,而後再讓 slow 變量和 fast 同步走,它們之間就會一直保持 k-1 的距離,因此當 fast 到鏈表尾結點的時候,slow 剛剛指向的是倒數第 k 個結點。this

本題因爲咱們要知道倒數第 k+1 個結點,因此得讓 fast 先走 k 步,待 fast 指向鏈表尾結點的時候,slow 正好指向倒數第 k+1 個結點。spa

咱們簡單思考一下臨界值:code

  1. 當 k = 1 的時候,刪除的值是尾結點。咱們讓 fast 先走 1 步,當 fast.next 爲尾結點的時候,倒數第 k+1 個結點正好是咱們的倒數第二個結點。咱們刪除 slow.next,並讓slow.next 指向 slow.next.next = null,知足條件。
  2. 當 k > len 的時候,咱們要找的倒數第 k 個元素不存在,直接出錯;
  3. 當 1 < k < len 的時候,k 最大爲 len-1 的時候,fast 移動 len-1 步,直接到達尾結點,此時,snow 指向頭結點。刪除倒數第 k 個元素,即刪除正數第 2 個結點便可;
  4. 當 k = len 的時候比較特殊,當 fast 移動 len 步的時候,已經指向了 fast.next = null,此時咱們其實要刪除的是頭結點,直接返回 head.next 便可。

因此咱們天然能獲得這樣的代碼。ci

public class Test07 { public static class LinkNode { int data; LinkNode next; public LinkNode(int data) { this.data = data; } } private static LinkNode delTheSpecifiedReverse(LinkNode head, int k) { LinkNode slow = head; LinkNode fast = head; if (fast == null) { throw new RuntimeException("your linkNode is null"); } // 先讓 fast 先走 k 步 for (int i = 0; i < k; i++) { if (fast == null) { // 說明輸入的 k 已經超過了鏈表長度,直接報錯 throw new RuntimeException("the value k is too large."); } fast = fast.next; } // fast == null ,說明已經到了尾結點後面的空區域,說明要刪除的就是頭結點。 if (fast == null) { return head.next; } while (fast.next != null) { slow = slow.next; fast = fast.next; } slow.next = slow.next.next; return head; } public static void main(String[] args) { LinkNode head = new LinkNode(1); head.next = new LinkNode(2); head.next.next = new LinkNode(3); head.next.next.next = new LinkNode(4); head.next.next.next.next = new LinkNode(5); LinkNode node = delTheSpecifiedReverse(head, 3); while (node != null) { System.out.print(node.data + "->"); node = node.next; } } } 

好了,咱們解決了昨天文章中留下的拓展題,今天咱們來看看咱們鏈表都還有些怎樣的考法。同步

面試題:定義一個單鏈表,輸入一個鏈表的頭結點,反轉該鏈表並輸出反轉後鏈表的頭結點。爲了方便,咱們鏈表的 data 採用整型。string

這是一道反轉鏈表的經典題,咱們來屢一下思路:一個結點包含下一結點的引用,反轉的意思就是要把原來指向下一結點的引用指向上一個結點。咱們能夠分爲下面的步驟:

  1. 找到當前要反轉的結點的下一個結點,並用變量保存,由於下一次要反轉的是它,若是咱們不保存的話必定會由於前面已經反轉,致使沒法經過遍歷獲得這個結點;
  2. 而後讓當前結點的 next 引用指向上一個結點,上一個結點初始 null 由於頭結點的反轉後變成尾結點;
  3. 當前要反轉的結點變成下一個要比較元素的上一個結點,用變量保存;
  4. 當前要比較的結點賦值爲以前保存的未反轉前的下一個結點;
  5. 當前反轉的結點爲 null 的時候,保存的上一個結點即反轉後的鏈表頭結點。

用代碼實現就是:

public class Test08 { private static class LinkNode { int data; LinkNode next; LinkNode(int data) { this.data = data; } } private static LinkNode reverseLink(LinkNode head) { // 上一個結點 LinkNode nodePre = null; LinkNode next = null; LinkNode node = head; while (node != null) { // 先用 next 保存下一個要反轉的結點,否則會致使鏈表斷裂。 next = node.next; // 再把如今結點的 next 引用指向上一個結點 node.next = nodePre; // 把當前結點賦值給 nodePre 變量,以便於下一次賦值 nodePre = node; // 向後遍歷 node = next; } return nodePre; } public static void main(String[] args) { LinkNode head = new LinkNode(1); head.next = new LinkNode(2); head.next.next = new LinkNode(3); head.next.next.next = new LinkNode(4); head.next.next.next.next = new LinkNode(5); LinkNode node = reverseLink(head); while (node != null) { System.out.print(node.data + "->"); node = node.next; } } } 

鏈表能夠考的可真多,相信不是小夥伴都和我同樣,雲裏霧裏了,那咱們今天就講到這裏,後面還要繼續考算法,你,打起精神,別睡着了。

相關文章
相關標籤/搜索