面試題15 鍊錶中倒數第K個結點

2022-03-06 16:42:52 字數 3437 閱讀 8961

題目:輸入乙個鍊錶,輸出該鍊錶中倒數第k個結點。為了符合大多數人的習慣,本題從1開始計數,即鍊錶的尾結點是倒數第1個結點。例如乙個鍊錶有6個結點,從頭結點開始它們的值依次是1、2、3、4、5、6。這個鍊錶的倒數第3個結點是值為4的結點。
看到這道題目,最直觀的想法,就是先算出鍊錶的長度n,然後倒數第k個結點就是順序的第(n-k+1)個數,不過這樣需要2次遍歷鍊錶,如果要求只能遍歷鍊錶一次,那麼上述演算法就不符合要求了。

那我們就使用第二種演算法,設定兩個指標p1和p2,兩個指標剛開始都指向鍊錶的第乙個結點,然後讓p1指標先走(k-1)步,然後再讓兩個指標一起往後走,當p1指標指向鍊錶最後乙個結點的時候,p2指標剛好指向鍊錶中的倒數第k個結點。

在寫**的時候需要考慮魯棒性,最好採用防禦性程式設計,就是考慮在哪些地方會出錯,然後提前加上錯誤判斷,這樣避免因此錯誤輸入而導致程式崩潰。

下面首先給出**例項,然後再講解程式魯棒性:

view code

#include#include

#include

using

namespace

std;

//鍊錶結構

struct

listnode;//

建立乙個鍊錶結點

listnode* createlistnode(int

value)

//遍歷鍊錶中的所有結點

void printlist(listnode*phead)

cout

<

輸出鍊錶中的某一結點的值

void printlistnode(listnode*pnode)

else}//

往鍊錶末尾新增結點

/*注意這裡phead是乙個指向指標的指標,在主函式中一般傳遞的是引用。

因為如果要為鍊錶新增結點,那麼就會修改鍊錶結構,所以必須傳遞引用才能夠儲存修改後的結構。

*/void addtotail(listnode** phead,int

value)

else}//

非防禦性程式設計,非法輸入會導致錯誤。

listnode* kthnodefromend(listnode* phead,int

k)

while(pnode->m_pnext!=null)

return

pkthnode;}//

防禦性程式設計,魯棒性更好

listnode* kthnodefromend2(listnode* phead,int

k)

else

return

null;

}while(pnode->m_pnext!=null)

return

pkthnode;

}void

main()

上述程式中,求倒數第k個結點的第乙個方法:listnode* kthnodefromend(listnode* phead,int k)。

這個方法看似滿足了我們的題目要求,但是當我們仔細分析,發現有3中方法讓程式崩潰

輸入的plisthead為空指標,由於**會嘗試訪問空指標指向的記憶體,程式崩潰

輸入的以plisthead為頭結點的鍊錶的結點少於k。這樣在while(k-1>0)的迴圈中,又會出現嘗試訪問空指標指向的記憶體。

輸入的引數k小於等於0。根據題意k的最小值應為1。

考慮上述因素,我們給出了第二個方法:listnode* kthnodefromend2(listnode* phead,int k)。

1.求鍊錶的中間結點。如果鍊錶中結點總數為奇數,返回中間結點;如果結點總數是偶數,返回中間兩個結點的任意乙個。為了解決這個問題,我們也可以定義兩個指標,同時從鍊錶的頭結點出發,乙個指標一次走一步,另乙個指標一次走兩步。當走得快的指標走到鍊錶的末尾時,走得慢的指標正好在鍊錶的中間。

**例項如下:

view code

#include#include

#include

using

namespace

std;

//鍊錶結構

struct

listnode;//

建立乙個鍊錶結點

listnode* createlistnode(int

value)

//遍歷鍊錶中的所有結點

void printlist(listnode*phead)

cout

<

輸出鍊錶中的某一結點的值

void printlistnode(listnode*pnode)

else}//

往鍊錶末尾新增結點

/*注意這裡phead是乙個指向指標的指標,在主函式中一般傳遞的是引用。

因為如果要為鍊錶新增結點,那麼就會修改鍊錶結構,所以必須傳遞引用才能夠儲存修改後的結構。

*/void addtotail(listnode** phead,int

value)

else}//

求鍊錶中間結點

listnode* midnodeinlist(listnode*phead)

return

pmidnode;

}void

main()

2.判斷乙個單向鍊錶是否形成了環狀結構。和前面的問題一樣,定義兩個指標,同時從鍊錶的頭結點出發,乙個指標一次走一步,另外乙個指標一次走兩步。如果走得快的指標追上了走得慢的指標,那麼鍊錶就是環狀結構;如果走得快的指標走到了鍊錶的末尾(m_pnext指向null)都沒有追上走得慢的指標,那麼鍊錶就不是環狀結構。

**例項如下:

view code

#include#include

#include

using

namespace

std;

//鍊錶結構

struct

listnode;//

建立乙個鍊錶結點

listnode* createlistnode(int

value)

//連線兩個結點

void connectlistnode(listnode* pcurrent,listnode*pnext)

else}//

遍歷鍊錶中的所有結點

void printlist(listnode*phead)

cout

<

輸出鍊錶中的某一結點的值

void printlistnode(listnode*pnode)

else}//

判斷鍊錶中是否有環

int islootlist(listnode*phead)

return0;

}void

main()

else

system(

"pause");

}

面試題15 鍊錶中倒數第K個結點

題目 輸入乙個鍊錶,輸出該鍊錶中倒數第k個結點。為了符合大多數人的習慣,本題從1開始計數,即鍊錶的尾結點是倒數第1個結點。例如乙個鍊錶有6個結點,從頭結點開始它們的值依次是1 2 3 4 5 6。這個鍊錶的倒數第3個結點是值為4的結點。鍊錶的節點定義如下,這裡使用的是c 來定義 public cla...

面試題15 鍊錶中倒數第k個結點

為了實現只遍歷鍊錶一次就能找到倒數第k 個結點,我們可以定義兩 個指標。第乙個指標從鍊錶的頭指標開始遍歷向前走k 1步,第二個指標保持不動 從第k 步開始,第二個指標也開始從鍊錶的頭指標開始遍歷。由於兩個指標的距離保持在k 1 當第乙個 走在前面的 指標到達鍊錶的尾結點時,第二個指標 走在後面的 指...

面試題15 鍊錶中倒數第K個結點

題目 輸入乙個鍊錶,輸出該鍊錶中倒數第k個結點。為了符合大多數人的習慣,本題從1開始計數,即鍊錶的尾結點是倒數第1個結點。例如乙個鍊錶有6個結點,從頭結點開始它們的值依次是1 2 3 4 5 6。這個鍊錶的倒數第3個結點是值為4的結點。看到這道題目,最直觀的想法,就是先算出鍊錶的長度n,然後倒數第k...