C++双向链表Node类remove函数异常问题排查求助
双向链表remove函数的问题分析与修复
问题背景
开发基于Node类的C++双向有序链表时,实现remove函数删除目标节点遇到以下异常:
- 待删除值大于currentPrt的data时,函数无任何操作;
- 待删除值小于currentPrt的data时,删除节点后程序陷入无限循环并打印乱码值。
相关代码
Node类代码
// Node.h class Node { public: explicit Node(int data = 0, Node *nextPtr = nullptr, Node *beforePtr = nullptr); int getData() const; void setData(int data); Node *getNextPtr() const; void setNextPtr(Node *nextPtr); Node *getBeforePtr() const; void setBeforePtr(Node *beforePtr); void print() const; private: int data; Node *nextPtr; Node *beforePtr; };
// Node.cpp #include <iostream> using namespace std; Node::Node(int data, Node *nextPtr, Node *beforePtr) : data(data), nextPtr(nextPtr), beforePtr(beforePtr) {} int Node::getData() const { return data; } void Node::setData(int data) { Node::data = data; } Node *Node::getNextPtr() const { return nextPtr; } void Node::setNextPtr(Node *nextPtr) { Node::nextPtr = nextPtr; } Node *Node::getBeforePtr() const { return beforePtr; } void Node::setBeforePtr(Node *beforePtr) { Node::beforePtr = beforePtr; } void Node::print() const { cout << getData() << endl; }
MyList类代码
// MyList.h class MyList { public: MyList(Node *currentPrt = nullptr); void insert(int value); void print() const; void remove(int value); private: Node *currentPrt; };
// MyList.cpp #include <iostream> #include "Node.h" using namespace std; MyList::MyList(){ currentPrt = nullptr; } void MyList::insert(int value) { if(currentPrt == nullptr){ currentPrt = new Node; currentPrt->setData(value); currentPrt->setNextPtr(nullptr); currentPrt->setBeforePtr(nullptr); } else{ if(value > currentPrt->getData()){ while (currentPrt->getNextPtr() != nullptr && currentPrt->getNextPtr()->getData() < value){ currentPrt = currentPrt->getNextPtr(); } Node *newPtr = new Node(value); newPtr->setNextPtr(currentPrt->getNextPtr()); if (currentPrt->getNextPtr() != nullptr) currentPrt->getNextPtr()->setBeforePtr(newPtr); currentPrt->setNextPtr(newPtr); newPtr->setBeforePtr(currentPrt); } else{ while (currentPrt->getBeforePtr() != nullptr && currentPrt->getBeforePtr()->getData() > value){ currentPrt = currentPrt->getBeforePtr(); } Node *newPtr = new Node(value); if (currentPrt->getBeforePtr() != nullptr){ currentPrt = currentPrt->getBeforePtr(); newPtr->setNextPtr(currentPrt->getNextPtr()); currentPrt->getNextPtr()->setBeforePtr(newPtr); currentPrt->setNextPtr(newPtr); newPtr->setBeforePtr(currentPrt); } else{ currentPrt->setBeforePtr(newPtr); newPtr->setNextPtr(currentPrt); } } } } void MyList::remove(int value) { if (currentPrt != nullptr){ if(value > currentPrt->getData()){ while (currentPrt->getNextPtr() != nullptr && currentPrt->getBeforePtr()->getData() > value){ currentPrt = currentPrt->getNextPtr(); } if (currentPrt->getNextPtr()->getData() == value){ delete currentPrt->getNextPtr(); } } else{ while (currentPrt->getBeforePtr() != nullptr && currentPrt->getBeforePtr()->getData() > value){ currentPrt = currentPrt->getBeforePtr(); } if (currentPrt->getBeforePtr()->getData() == value){ delete currentPrt->getBeforePtr(); } } } } void MyList::print() const { Node *ptr; ptr = currentPrt; while(ptr->getNextPtr() != nullptr){ ptr = ptr->getNextPtr(); } for (ptr; ptr != nullptr; ptr = ptr->getBeforePtr()){ cout << ptr->getData() << endl; } }
测试代码
#include "MyList.h" #include <iostream> using namespace std; int main() { MyList test; test.insert(5); test.insert(3); test.insert(7); test.insert(6); test.print(); std::cout<<std::endl; test.remove(7); // 无效果但不触发无限循环 test.remove(5); // 无效果但不触发无限循环 test.remove(6); // 触发无限循环打印 test.remove(3); // 触发无限循环打印 test.print(); return 0; }
测试输出
仅执行remove(5)、remove(7)时,输出无变化:
3 5 6 7 3 5 6 7
执行remove(6)或remove(3)时,程序陷入无限循环打印乱码值:
2109940880 2109365888 2109348064 2109342032
问题分析
你的remove函数存在多个致命逻辑错误:
查找目标节点的循环条件完全错误
- 当
value > currentPrt->getData()时,循环条件写成了currentPrt->getBeforePtr()->getData() > value,与查找方向完全矛盾,应该遍历后续节点直到找到大于等于value的位置,正确条件应为currentPrt->getNextPtr()->getData() < value。 - 这个错误导致永远找不到目标节点,所以删除大于currentPrt值的节点时无任何操作。
- 当
删除节点时未维护链表指针关系
- 直接
delete currentPrt->getNextPtr()或delete currentPrt->getBeforePtr()后,没有更新前后节点的指针指向。比如删除currentPrt的前节点时,currentPrt的beforePtr仍然指向已被释放的内存,被删节点的前节点的nextPtr也没有指向currentPrt,这会导致链表出现野指针,打印时访问非法内存,出现乱码甚至无限循环。
- 直接
未处理目标节点就是currentPrt本身的情况
- 当待删除值等于currentPrt的data时,
if(value > currentPrt->getData())和else分支都不会执行,所以remove(5)完全没有操作。
- 当待删除值等于currentPrt的data时,
空指针访问风险
- 在判断
currentPrt->getNextPtr()->getData() == value前,没有检查currentPrt->getNextPtr()是否为空,若链表遍历到末尾,会导致空指针访问崩溃。
- 在判断
修复后的remove函数
void MyList::remove(int value) { if (currentPrt == nullptr) return; Node* target = nullptr; // 先找到目标节点 if (value == currentPrt->getData()) { target = currentPrt; } else if (value > currentPrt->getData()) { Node* temp = currentPrt; while (temp->getNextPtr() != nullptr) { if (temp->getNextPtr()->getData() == value) { target = temp->getNextPtr(); break; } else if (temp->getNextPtr()->getData() > value) { // 有序链表,后续节点更大,直接退出 break; } temp = temp->getNextPtr(); } } else { Node* temp = currentPrt; while (temp->getBeforePtr() != nullptr) { if (temp->getBeforePtr()->getData() == value) { target = temp->getBeforePtr(); break; } else if (temp->getBeforePtr()->getData() < value) { // 有序链表,前序节点更小,直接退出 break; } temp = temp->getBeforePtr(); } } if (target == nullptr) return; // 未找到目标节点 // 维护链表指针关系 Node* prevNode = target->getBeforePtr(); Node* nextNode = target->getNextPtr(); if (prevNode != nullptr) { prevNode->setNextPtr(nextNode); } if (nextNode != nullptr) { nextNode->setBeforePtr(prevNode); } // 如果删除的是currentPrt,更新currentPrt指向 if (target == currentPrt) { currentPrt = nextNode != nullptr ? nextNode : prevNode; } delete target; // 释放内存 }
额外优化建议
- MyList类的构造函数声明和实现不一致:头文件里是
MyList(Node *currentPrt = nullptr);,实现里是MyList::MyList(){ currentPrt = nullptr; },建议统一。 - 原测试代码里的
printAscending()应为print(),因为MyList类中未声明printAscending成员函数。 - 建议给MyList添加析构函数,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Yusuf Halim
相关产品推荐
相关产品推荐

