某本書上面說了,鍊錶這個東西,實際用的並不多,但是可以提供很好的考察面試者程式設計技巧和思維能力的素材。這裡總結一下,見過的面試題和對應的候選解法。
題一、 給定單鏈表,檢測是否有環。
使用兩個指標p1,p2從煉表頭開始遍歷,p1每次前進一步,p2每次前進兩步。如果p2到達鍊錶尾部,說明無環,否則p1、p2必然會在某個時刻相遇(p1==p2),從而檢測到鍊錶中有環。
這篇文章講了很多好的壞得相關演算法。
題二、 給定兩個單鏈表(head1, head2),檢測兩個鍊錶是否有交點,如果有返回第乙個交點。
如果head1==head2,那麼顯然相交,直接返回head1。
否則,分別從head1,head2開始遍歷兩個鍊錶獲得其長度len1與len2。假設len1>=len2,那麼指標p1由head1開始向後 移動len1-len2步。指標p2=head2,下面p1、p2每次向後前進一步並比較p1p2是否相等,如果相等即返回該結點,否則說明兩個鍊錶沒有 交點。
題三、 給定單鏈表(head),如果有環的話請返回從頭結點進入環的第乙個節點。
運用題一,我們可以檢查鍊錶中是否有環。
如果有環,那麼p1p2重合點p必然在環中。從p點斷開環,方法為:p1=p, p2=p->next, p->next=null。此時,原單鏈表可以看作兩條單鏈表,一條從head開始,另一條從p2開始,於是運用題二的方法,我們找到它們的第乙個交點即為所求。
也可以不斷開環。設重合點為p3,從p3開始遍歷這個環,同時從表頭開始走,檢查每步是否在那個環中。這個方法大概有nlogn。
使用快慢指標,第一次相遇,表明存在迴圈。繼續快慢指標,第二次相遇,得到的iteration步長為環的長度。分別從相遇點和第乙個節點出發,都是步長為1的指標,當相遇時,得到的iteration步長為環首的位置。
題
四、只給定單鏈表中某個結點p(並非最後乙個結點,即p->next!=null)指標,刪除該結點。
辦法很簡單,首先是放p中資料,然後將p->next的資料copy入p中,接下來刪除p->next即可。
題
五、只給定單鏈表中某個結點p(非空結點),在p前面插入乙個結點。
辦法與前者類似,首先分配乙個結點q,將q插入在p後,接下來將p中的資料copy入q中,然後再將要插入的資料記錄在p中。
題
六、給定單鏈表頭結點,刪除鍊錶中倒數第k個結點。
使用兩個節點p1,p2,p1初始化指向頭結點,p2一直指向p1後第k個節點,兩個結點平行向後移動直到p2到達鍊錶尾部(null),然後根據p1刪除對應結點。
題
七、鍊錶排序
鍊錶排序最好使用歸併排序演算法。堆排序、快速排序這些在陣列排序時效能非常好的演算法,在鍊錶只能「順序訪問」的魔咒下無法施展能力;但是歸併排序卻如魚得水,非但保持了它o(nlogn)的時間複雜度,而且它在陣列排序中廣受詬病的空間複雜度在鍊錶排序中也從o(n)降到了o(1)。真是好得不得了啊,哈哈。以上程式是遞推法的程式,另外值得一說的是看看那個時間複雜度,是不是有點眼熟?對!這就是分治法的時間複雜度,歸併排序又是divide and conquer。
double cmp(listnode *p ,listnode *q)
listnode* mergesortlist(listnode *head)
while (1)
qsize = nstep;
while (psize >0 || (qsize >0 && q))
elseif (q == null || qsize == 0)
elseif (cmp(p,q) <= 0)
else
if (tail != null)
else
tail = e;
} p = q;
} tail->next = null;
if (nmerges <= 1)
else
} }題
八、倒轉單鏈表
給出非遞迴和遞迴解法:
#include
using
namespace
std;
struct
node
*head
;// 非遞迴寫法
node
*inverselinkedlist
(node
*head
)return
newhead;}
// 遞迴寫法
node
*inverselinkedlistrecur
(node
*head
) tmp
->
next
=head
; head
->
next
=null
;return
newhead;}
intmain
() head
=inverselinkedlist
(head
);node
*tmp
=head
;while
(head
!=null
) cout
<<
endl
; head
=inverselinkedlistrecur
(tmp
); tmp
=head
;while
(head
!=null
) cout
<<
endl
;while
(tmp
!=null)}
題
九、找出鍊錶的中間元素
單鏈表的乙個比較大的特點用一句廣告語來說就是「不走回頭路」,不能實現隨機訪問(random access)。如果我們想要找乙個陣列a的中間元素,直接a[len/2]就可以了,但是鍊錶不行,因為只有a[len/2 - 1] 知道a[len/2]在哪兒,其他人不知道。因此,如果按照陣列的做法依樣畫葫蘆,要找到鍊錶的中點,我們需要做兩步(1)知道鍊錶有多長(2)從頭結點開始順序遍歷到鍊錶長度的一半的位置。這就需要1.5n(n為鍊錶的長度)的時間複雜度了。有沒有更好的辦法呢?有的。想法很簡單:兩個人賽跑,如果a的速度是b的兩倍的話,當a到終點的時候,b應該剛到中點。這只需要遍歷一遍鍊錶就行了,還不用計算鍊錶的長度。
鏈表面試題
不改變鍊錶結構,從尾到頭列印單鏈表 遞迴實現 void printlistrevers recursively plist phead printf d phead data 當鍊表非常長的時候,遞迴實現的會導致函式呼叫層級很深,可能導致呼叫棧溢位。用棧不會出現此類情況,顯然用棧實現 的魯棒性會好一...
鏈表面試題
從尾到頭列印單鏈表 棧 遞迴 從尾到頭列印鍊錶 includevoid printtailtohead listnode head while s.empty 空間複雜度o n void printtailtoheadr listnode head cout data 刪除乙個無頭單鏈表的非尾結點 ...
鏈表面試題
typedef int datatype typedef struct node node,pnode,plist void initlist plist pplist void display const plist plist void pushback plist pplist,datatyp...