为了方便链表的增删操作,一些算法生成一个虚拟头节点,方便统一删除头节点和其他节点。下面代码实现了删除链表中值为 val 的节点,横线上应填的最佳代码是( )。
struct ListNode {
int val;
ListNode* next;
ListNode(int val):val(val), next(nullptr){}
};
void removeElements(ListNode* head, int val) {
if (head == nullptr) {
return;
}
ListNode* cur;
ListNode* dummyHead = new ListNode(0); //虚拟头节点
// 在此处填入代码
while(cur ->next != nullptr) {
if(cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp = nullptr;
}
else {
cur = cur ->next;
}
}
head = dummyHead->next;
delete dummyHead;
dummyHead = nullptr;
}根据上下文和C++语法规则确定空缺处应填的内容,关注函数调用参数、表达式和返回值类型。
填空时注意语法完整性:分号、括号匹配、函数参数类型和个数、返回值类型。
【考点】链表操作|虚拟头节点|指针赋值
🎯 考纲定位 GESP 五级「数据结构与算法」链表专项。命题人卡住的是考生对虚拟头节点初始化的理解——多数人会忽略 cur 起始位置,导致首节点无法被正确遍历。
🧠 解题思维 虚拟头节点的本质是让 cur 始终指向待删节点的前驱,初始化必须把 dummyHead->next 接上原链表,cur 指向 dummyHead 才能遍历。这样循环里始终操作 cur->next,无需单独判断头节点是否要删。
🪤 陷阱拆解 B 选项跳过了首节点,误以为虚拟头节点要直接接第二个;C 和 D 把 cur 指向了真正头节点,导致循环里的 cur->next 永远查不到第一个元素——若第一个值就要删,程序直接跳过。只有 A 让 cur 停在虚拟头,cur->next 才是链表第一个真节点,满足题意。
🔄 反命题思维 若去掉虚拟头节点,代码得在循环外单独判断头节点,且每次删除头节点都要更新 head 指针——这会让逻辑变得繁琐。命题人正是用这道题,考察你是否理解虚拟头节点的本质是简化边界处理。
— ()