鏈表面試題小結

2021-06-07 22:22:55 字數 3389 閱讀 3154

某本書上面說了,鍊錶這個東西,實際用的並不多,但是可以提供很好的考察面試者程式設計技巧和思維能力的素材。這裡總結一下,見過的面試題和對應的候選解法。

題一、 給定單鏈表,檢測是否有環。

使用兩個指標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...