今天在做leetcode203:移除鏈表元素時,反復遇到了報錯:runtime error: member access within null pointer of type ‘ListNode’ (solution.cpp),報錯提示的意思是試圖訪問’ListNode空指針類型的成員,就淺淺記錄一下修復bug的過程吧。。。。
剛開始的代碼是這樣的,邏輯是先建立一個頭結(jié)點放到鏈表頭部,這樣就可以統(tǒng)一鏈表結(jié)點刪除的操作了,然后創(chuàng)建ListNode類型指針cur,初始化其指向頭結(jié)點的下一個結(jié)點,利用while循環(huán)遍歷鏈表,當cur指針指向Null時停止遍歷。然后就報錯了…
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyNode=new ListNode(0);
dummyNode->next=head;
ListNode* cur=dummyNode->next;
ListNode* tmp=nullptr;
while(cur!=nullptr){
if(cur->val==val){
tmp=cur->next;
cur->next=cur->next->next;
delete tmp;
cur=cur->next;
}else cur=cur->next;
}
head=dummyNode->next;
delete dummyNode;
return head;
}
};
報錯的代碼行是:cur->next=cur->next->next;
,報錯提示的意思是試圖訪問’ListNode空指針類型的成員,然后才發(fā)現(xiàn)原因出在:當移除的元素位于鏈表最后一個時,此時cur指向最后一個結(jié)點,cur->next為空,而訪問cur->nex->next就是訪問空指針的成員變量了。
那么怎么解決這個問題呢?我最開始想到的辦法是是增加判斷語句,當cur指向的是最后一個結(jié)點時,就不執(zhí)行cur->next=cur->next->next;
操作,而是將前一個結(jié)點的next指針置為空,但是因為cur指向的是當前結(jié)點,所以沒有辦法操作上一個結(jié)點,這個辦法好像行不通。
于是我參考了代碼隨想錄中的參考答案如下:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0); // 設置一個虛擬頭結(jié)點
dummyHead->next = head; // 將虛擬頭結(jié)點指向head,這樣方面后面做刪除操作
ListNode* cur = dummyHead;
while (cur->next != NULL) {
if(cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
可以發(fā)現(xiàn)這個代碼與我的代碼不同有兩點:第一是cur指針初始化時指向的是頭結(jié)點,而我的代碼cur指向是指向頭結(jié)點的下一個結(jié)點,即原始鏈表的第一個結(jié)點,第二是while循環(huán)中的條件為cur->next != NULL
,即當cur結(jié)點的下一個結(jié)點不為空時繼續(xù)循環(huán),而我的代碼是cur!=nullptr
,即當前結(jié)點不為空時繼續(xù)循環(huán)。
區(qū)別感覺很小,但是代碼隨想錄中的這份代碼并不會報錯,原因就在這份代碼的循環(huán)條件是cur->next != NULL
,這樣保證了cur->next
不為空,因此就不會出現(xiàn)訪問空指針的成員變量的情況;而且判斷某結(jié)點的值時利用的是cur->next ->val
進行判斷,而不是讓cur指針直接指向該結(jié)點,即利用cur->val
判斷,=這樣做的好處是判斷某一個結(jié)點的值時同時也能保留對其上一個結(jié)點進行修改的權利,這樣當需要刪除的元素在鏈表中最后一個時,可以很方便的將其前一個結(jié)點的next指針置為nullptr。
于是我參考代碼隨想錄把原始代碼改成了下面的代碼,但是一時腦癱的在if(cur->next->val==val)
的作用域最后多加了一句cur=cur->next;
,以為此時指針也要后移一位,結(jié)果又遇到了同樣的報錯,而且報錯轉(zhuǎn)移到了if(cur->next->val==val)
這一行,最后才反應過來,如果待刪除元素位于鏈表最后,當執(zhí)行完cur->next=cur->next->next
時,cur->next
為空,如果這時執(zhí)行cur=cur->next;
,則cur為空,等到下一次循環(huán)判斷時,又會訪問空指針成員變量,因此這一句不能加,同一個坑踩兩次了屬于是…而且在需要刪除的結(jié)點情況下,cur指針不用后移,因為下一次循環(huán)cur->next訪問的就是被刪除節(jié)點的下一個結(jié)點了,這個邏輯當時也沒理清。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyNode=new ListNode(0);
dummyNode->next=head;
ListNode* cur=dummyNode;
ListNode* tmp=nullptr;
while(cur->next!=nullptr){
if(cur->next->val==val){
tmp=cur->next;
cur->next=cur->next->next;
delete tmp;
cur=cur->next;
}else cur=cur->next;
}
head=dummyNode->next;
delete dummyNode;
return head;
}
};
最后總結(jié)來說:
1.利用讓cur指針的移動范圍為:[頭結(jié)點,倒數(shù)第二個結(jié)點],比起范圍為[第二個結(jié)點,最后一個結(jié)點]更加合理,可以有效處理待刪除元素在鏈表尾部的問題,也能夠避免bug的發(fā)生。
2.使用鏈表時要嚴格檢查有沒有訪問空指針的成員,特別要考慮待刪除元素在鏈表邊界的特殊情況。文章來源:http://www.zghlxwxcb.cn/news/detail-675483.html
ps:如果細心的話可以發(fā)現(xiàn)代碼隨想錄中判斷空指針用的是Null,而我的代碼里用的是nullptr,當時覺得有些疑惑,這里再簡單總結(jié)一下在C++中Null和nullptr的區(qū)別:用Null表示空指針是C語言中遺留下來的傳統(tǒng),但在C++中可能會引起問題,因此在C++11中引入了nullptr表示空指針,如果要在C++中表示空指針,那么使用nullptr而不是Null.。文章來源地址http://www.zghlxwxcb.cn/news/detail-675483.html
到了這里,關于leetcode鏈表題報錯 runtime error: member access within null pointer of type ‘ListNode‘的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!