You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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函数存在多个致命逻辑错误:

  1. 查找目标节点的循环条件完全错误

    • 当value > currentPrt->getData()时,循环条件写成了currentPrt->getBeforePtr()->getData() > value,与查找方向完全矛盾,应该遍历后续节点直到找到大于等于value的位置,正确条件应为currentPrt->getNextPtr()->getData() < value。
    • 这个错误导致永远找不到目标节点,所以删除大于currentPrt值的节点时无任何操作。
  2. 删除节点时未维护链表指针关系

    • 直接delete currentPrt->getNextPtr()或delete currentPrt->getBeforePtr()后,没有更新前后节点的指针指向。比如删除currentPrt的前节点时,currentPrt的beforePtr仍然指向已被释放的内存,被删节点的前节点的nextPtr也没有指向currentPrt,这会导致链表出现野指针,打印时访问非法内存,出现乱码甚至无限循环。
  3. 未处理目标节点就是currentPrt本身的情况

    • 当待删除值等于currentPrt的data时,if(value > currentPrt->getData())和else分支都不会执行,所以remove(5)完全没有操作。
  4. 空指针访问风险

    • 在判断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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 14:45:26