題目:輸入乙個鍊錶,輸出該鍊錶中倒數第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上述程式中,求倒數第k個結點的第乙個方法:listnode* kthnodefromend(listnode* phead,int k)。#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()
這個方法看似滿足了我們的題目要求,但是當我們仔細分析,發現有3中方法讓程式崩潰
輸入的plisthead為空指標,由於**會嘗試訪問空指標指向的記憶體,程式崩潰
輸入的以plisthead為頭結點的鍊錶的結點少於k。這樣在while(k-1>0)的迴圈中,又會出現嘗試訪問空指標指向的記憶體。
輸入的引數k小於等於0。根據題意k的最小值應為1。
考慮上述因素,我們給出了第二個方法:listnode* kthnodefromend2(listnode* phead,int k)。
1.求鍊錶的中間結點。如果鍊錶中結點總數為奇數,返回中間結點;如果結點總數是偶數,返回中間兩個結點的任意乙個。為了解決這個問題,我們也可以定義兩個指標,同時從鍊錶的頭結點出發,乙個指標一次走一步,另乙個指標一次走兩步。當走得快的指標走到鍊錶的末尾時,走得慢的指標正好在鍊錶的中間。
**例項如下:
view code
#include#include2.判斷乙個單向鍊錶是否形成了環狀結構。和前面的問題一樣,定義兩個指標,同時從鍊錶的頭結點出發,乙個指標一次走一步,另外乙個指標一次走兩步。如果走得快的指標追上了走得慢的指標,那麼鍊錶就是環狀結構;如果走得快的指標走到了鍊錶的末尾(m_pnext指向null)都沒有追上走得慢的指標,那麼鍊錶就不是環狀結構。#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()
**例項如下:
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...