今天國慶節,祝大家中秋節快樂,順便給大家拜個早年[狗頭]。不過最近還在準備面試的同學們不要浪太狠,還是要好好學習的鴨。
單鏈表的排序在資料結構類的面試題中簡直是集大成者,什麼排序、鍊錶、鍊錶刪除、新增…… 都能體現在單鏈表排序上,也非常考驗候選者的程式設計基本功,思路說起來很簡單,但能不能寫出來又是另外一回事了。
有些人覺得面試面這種題意義不大,誰實際工作中會寫過單鏈表的排序,不都是直接調collections.sort()嗎? 是,沒錯 是這樣,也許對某些人而言,他會這道題和不會這道題對將來的工作產生不了任何影響,這就需要非常長的時間去驗證了,顯然招聘者等不了那麼久,他們只想花最少的時間找到你的上限,摸到你的上限後他們就可以簡單假設這條線下面的其他東西你都會了,雖然這種假設有侷限性,會讓那種恰巧準備了的人佔了便宜,但這種方法卻不失為成本最低的方法。這就好比高考一樣,高考所考的內容大多數人一輩子都用不上,但高考仍有存在的意義。
扯遠了,回到正題,單鏈表排序設計到的知識點都是大學本科資料結構裡講過的,所以對應屆生而言這題完全不會超綱。對面試官而言,你能解釋清楚思路 說明你在校資料結構學的還可以,你再能把你思路寫出來,就能向面試官證明你程式設計能力可以。 (這裡有個面試小技巧:知道思路不會寫,先把思路給面試官講一遍,你考數學寫個解:還能得0.5分呢)
單鏈表排序可以用哪些排序演算法? 我的回答是所有排序演算法都可以用,但有些排序會相對簡單些,本文我給出三種(選擇、快排、歸併)方法,剩餘的幾種排序演算法有興趣你可以自己實現下,當然有些可能會比較繁瑣,是時候挑戰下自己了[狗頭]。這裡我簡化下題目,節點值為int整數,然後鍊錶按增序排列。
這裡先給出單鏈表節點類
public class linkednode
public linkednode(int val)
}
選擇排序的思路也很簡單,每次從原煉表中摘掉最小的乙個節點,拼接到新鍊錶中,直到原煉表摘乾淨。
public class selectsort implements sortstrategy
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
// 新建了個頭節點方便處理,否則就需要很多if來判斷了
linkednode l1 = new linkednode();
linkednode l2 = new linkednode();
linkednode p1 = l1;
linkednode p2 = l2;
linkednode p = head;
// 將原鍊錶一分為二,奇數編號節點在l1,偶數編號在l2
while (p != null)
p1.next = p;
p1 = p1.next;
if (p.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 (l2 == null)
linkednode newhead = new linkednode();
linkednode p = newhead;
linkednode p1 = l1;
linkednode p2 = l2;
while (p1 != null && p2 != null) else
p = p.next;
}while (p1 != null)
while (p2 != null)
return newhead.next;}}
快排整體的思路和歸併差不多,都是拆分、遞迴、合併,但其拆分就要比歸併的拆分策略複雜些。在上文歸併演算法中,我們只是簡單將鍊錶按奇偶變化拆分成了兩個鍊錶,但快排的拆分需要選擇乙個節點作為基準值,比它小的拆到左鍊錶,反之的拆到右鍊錶,然後遞迴對左右兩個鍊錶排序,最後合併。但它的合併就簡單了,只需要 左鍊錶+基準節點+又鍊錶簡單拼接在一起就可以了。
如圖所示,黃色為我選中的基準節點(鍊錶的頭節點),紅色為未排序鍊錶,藍色為排序後的鍊錶,紅色部分從上往下是拆分的過程,藍色部分從上往下是合併的過程。具體**實現如下:
public class quicksort implements sortstrategy
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) else
p = pn;
}// 遞迴對兩條鍊錶進行排序
left.next = sort(left.next);
right.next = sort(right.next);
// 先把又鍊錶拼到base後面
base.next = right.next;
// 左鍊錶+基準節點+右鍊錶拼接,左煉表有可能是空,所以需要特殊處理下
if (left.next != null)
// 把base拼接到左鍊錶的末尾
p.next = base;
return left.next;
} else }}
單鏈相關的題,已經爛大街了,具體參考leetcode top100 鍊錶題
排序相關:第k大的數,上文中快排可能出現的問題以及如何解決?(提示下,如果輸入資料全為降序會怎麼樣)
歸併:用一台2g記憶體的機器排序10個1g的檔案。
面試題 單鏈表快速排序
給定乙個單鏈表,請使用快速排序演算法對其排序 要求 期望平均時間複雜度為o nlogn 期望額外空間複雜度為o logn 思考題 如果只能改變鍊錶結構,不能修改每個節點的val值該如何做呢 資料範圍 鍊錶中的所有數大小均在int範圍內,鍊錶長度在 0,10000 輸入樣例 5,3,2 輸出樣例 2,...
開心IT面試題 單鏈表排序 反轉
一 單鏈表排序 仿照基於陣列的氣泡排序演算法 node sorting linklist node node node result int temp 0 int len length linklist node result node for int j 0 j len j result resu...
程式設計師面試題精選 歸併排序
採用分治策略 一般有三個步驟 1 分解 將n個元素分成各含n 2個元素的子串行 2 解決 用合併排序法對兩個子串行遞迴的排序 3 合併 合併兩個已排序的子串行以得到排序結果。在歸併排序時,其長度為1時遞迴結束。單個元素被視為是已排序好的。includeusing namespace std defi...