今天國慶節,祝你們中秋節快樂,順便給你們拜個早年[狗頭]。不過最近還在準備面試的同窗們不要浪太狠,仍是要好好學習的鴨。html
單鏈表的排序在數據結構類的面試題中簡直是集大成者,什麼排序、鏈表、鏈表刪除、添加…… 都能體如今單鏈表排序上,也很是考驗候選者的編程基本功,思路提及來很簡單,但能不能寫出來又是另一回事了。java
有些人以爲面試面這種題意義不大,誰實際工做中會寫過單鏈表的排序,不都是直接調Collections.sort()嗎? 是,沒錯 是這樣,也許對某些人而言,他會這道題和不會這道題對未來的工做產生不了任何影響,這就須要很是長的時間去驗證了,顯然招聘者等不了那麼久,他們只想花最少的時間找到你的上限,摸到你的上限後他們就能夠簡單假設這條線下面的其餘東西你都會了,雖然這種假設有侷限性,會讓那種恰巧準備了的人佔了便宜,但這種方法卻不失爲成本最低的方法。這就比如高考同樣,高考所考的內容大多數人一生都用不上,但高考仍有存在的意義。面試
扯遠了,回到正題,單鏈表排序設計到的知識點都是大學本科數據結構裏講過的,因此對應屆生而言這題徹底不會超綱。對面試官而言,你能解釋清楚思路 說明你在校數據結構學的還能夠,你再能把你思路寫出來,就能向面試官證實你編程能力能夠。 (這裏有個面試小技巧:知道思路不會寫,先把思路給面試官講一遍,你考數學寫個解:還能得0.5分呢)算法
單鏈表排序能夠用哪些排序算法? 個人回答是全部排序算法均可以用,但有些排序會相對簡單些,本文我給出三種(選擇、快排、歸併)方法,剩餘的幾種排序算法有興趣你能夠本身實現下,固然有些可能會比較繁瑣,是時候挑戰下本身了[狗頭]。這裏我簡化下題目,節點值爲int整數,而後鏈表按增序排列。編程
這裏先給出單鏈表節點類數據結構
public class LinkedNode { public int val; public LinkedNode next; public LinkedNode() { this(-1); } public LinkedNode(int val) { this.val = val; } }
選擇排序的思路也很簡單,每次從原鏈表中摘掉最小的一個節點,拼接到新鏈表中,直到原鏈表摘乾淨。ide
public class SelectSort implements SortStrategy { @Override public LinkedNode sort(LinkedNode head) { LinkedNode vHead = new LinkedNode(-1); vHead.next = head; // 增長虛擬頭節點,方便操做,不然就須要用一堆if來判斷了,代碼會比較囉嗦 LinkedNode newHead = new LinkedNode(-1); LinkedNode tail = newHead; // tail指向新鏈表的末尾 // 每次從鏈表中摘出來最小的節點,拼接到新鏈表末尾 while (vHead.next != null) { LinkedNode pre = vHead; LinkedNode cur = head; LinkedNode min = head; LinkedNode minPre = vHead; // 先遍歷找最小的節點,記錄下最小節點和它前面一個節點 while (cur != null) { if (cur.val < min.val) { minPre = pre; min = cur; } pre = cur; cur = cur.next; } // 把min節點從原鏈表中摘除,並拼接到新鏈表中 tail.next = min; tail = tail.next; minPre.next = min.next; } return newHead.next; } }
我我的感受歸併實際上是最適合作單鏈表排序的算法,雖然代碼稍微長有些,但思路清晰、好理解,並且時間複雜度只有O(nlogn)。歸併的思路能夠分爲3個部分。學習
public class MergeSort implements SortStrategy { @Override public LinkedNode sort(LinkedNode head) { // 遞歸邊界,若是有鏈表只有一個節點就不必排序了 if (head == null || head.next == null) { return head; } // 新建了個頭節點方便處理,不然就須要不少if來判斷了 LinkedNode l1 = new LinkedNode(); LinkedNode l2 = new LinkedNode(); LinkedNode p1 = l1; LinkedNode p2 = l2; LinkedNode p = head; // 將原鏈表一分爲二,奇數編號節點在l1,偶數編號在l2 while (p != null) { LinkedNode pnn = null; if (p.next != null) { pnn = p.next.next; } p1.next = p; p1 = p1.next; if (p.next != null) { p2.next = p.next; p2 = p2.next; p2.next = null; } p1.next = null; p = pnn; } // 遞歸將兩個鏈表作歸併排序. l1 = sort(l1.next); l2 = sort(l2.next); // 合併兩個排序好的有序鏈表 return merge(l1, l2); } // 合併兩個有序鏈表 private LinkedNode merge(LinkedNode l1, LinkedNode l2) { if (l1 == null) { return l2; } if (l2 == null) { return l1; } LinkedNode newHead = new LinkedNode(); LinkedNode p = newHead; LinkedNode p1 = l1; LinkedNode p2 = l2; while (p1 != null && p2 != null) { if (p1.val < p2.val) { p.next = p1; p1 = p1.next; } else { p.next = p2; p2 = p2.next; } p = p.next; } while (p1 != null) { p.next = p1; p1 = p1.next; p = p.next; } while (p2 != null) { p.next = p2; p2 = p2.next; p = p.next; } return newHead.next; } }
快排總體的思路和歸併差很少,都是拆分、遞歸、合併,但其拆分就要比歸併的拆分策略複雜些。在上文歸併算法中,咱們只是簡單將鏈表按奇偶變化拆分紅了兩個鏈表,但快排的拆分須要選擇一個節點做爲基準值,比它小的拆到左鏈表,反之的拆到右鏈表,而後遞歸對左右兩個鏈表排序,最後合併。但它的合併就簡單了,只須要 左鏈表+基準節點+又鏈表簡單拼接在一塊兒就能夠了。
ui
如圖所示,黃色爲我選中的基準節點(鏈表的頭節點),紅色爲未排序鏈表,藍色爲排序後的鏈表,紅色部分從上往下是拆分的過程,藍色部分從上往下是合併的過程。具體代碼實現以下:this
public class QuickSort implements SortStrategy { @Override public LinkedNode sort(LinkedNode head) { if (head == null || head.next == null) { return head; } LinkedNode left = new LinkedNode(); LinkedNode right = new LinkedNode(); LinkedNode p1 = left; LinkedNode p2 = right; LinkedNode p = head.next; LinkedNode base = head; // 選取頭節點爲基準節點 base.next = null; // 剩餘節點中比基準值小就放left裏,不然放right裏,按照大小拆分爲兩條鏈表 while (p != null) { LinkedNode pn = p.next; p.next = null; if (p.val < base.val) { p1.next = p; p1 = p1.next; } else { p2.next = p; p2 = p2.next; } p = pn; } // 遞歸對兩條鏈表進行排序 left.next = sort(left.next); right.next = sort(right.next); // 先把又鏈表拼到base後面 base.next = right.next; // 左鏈表+基準節點+右鏈表拼接,左鏈表有多是空,因此須要特殊處理下 if (left.next != null) { p = left.next; // 找到左鏈表的最後一個節點 while (p.next != null) { p = p.next; } // 把base拼接到左鏈表的末尾 p.next = base; return left.next; } else { return base; } } }
面試官也是要偷懶的,他們也懶得想題,再加上人的思惟是具備連續性的,這就意味着大機率下一道面試題(若有)會和這道題相關,我總結這道題能夠擴展的3個關鍵詞單鏈表、排序、歸併,基本上下一題都是這三個詞的發散,這裏我說下我能夠發散出的題目。
歡迎關注個人面試專欄面試題精選,永久免費 持續更新,本專欄會收集我遇到的比較經典面試題,除了提供詳盡的解法外還會從面試官的角度提供擴展題,但願能幫助你們找到更好的工做。另外,也徵集面試題,若是你遇到了不會的題 私信告訴我,有價值的題我會給你出一篇博客。
本文來自https://blog.csdn.net/xindoo