删除单链表中间的元素?

时间:2016-01-24 18:59:32

标签: c++ list data-structures linked-list

我目前正在实施链接列表,而且我在删除元素的功能上遇到了一些麻烦。以下是功能的全部内容。如果列表为空,我退出程序。如果列表只有一个元素,那么我只使用另一个功能正常的函数pop_back();。最后,如果元素位于列表的中间,我会遍历列表,直到找到它之前的链接。然后,我将指针指向下一个链接,指向要擦除的节点的下一个链接。该函数确实删除了一个元素,但它遇到了问题。

template <class T>
void List<T>::erase(iterator & pos) {
    Node<T> * currentNode = pos.current;
    Node<T> * nextNode = pos.current->next;
    Node<T> * searchNode = head;

    if (currentNode == 0) {
        cout << "LIST EMPTY. (ERASE)" << endl;
        exit(0);
    }
    else if (nextNode == 0) {
        pop_back();
    }
    else {
        while (searchNode->next != currentNode) {
            searchNode = searchNode->next;
        }
        searchNode->next = nextNode;
        delete currentNode;
        listsize--;
    }
}

我使用下面的测试代码来确保我的功能正常工作,但是从列表中删除元素后它似乎会中断。

int main() {
    List<int> l;
    l.push_front(5);
    l.push_front(3);
    l.push_back(31);
    l.push_back(354);
    l.push_front(63);
    l.displayList();
    for (Listiterator<int>::iterator it = l.begin(); it != l.end(); it++) {
        if (*it == 31) {
            l.erase(it);
            l.displayList();
        }
    }
    l.displayList();
    return 0;
}

以下是代码所经历的步骤:

63 -> 3 -> 5 -> 31 -> 354 -> NULL
(ERASE ELEMENT 31)
63 -> 3 -> 5 -> 354 -> NULL

通过调试器,我能够看到删除31后,迭代器位于内存中的NULL /非法位置并终止。

有没有人有任何想法或建议?

以下是重载的++运算符:

template <class T>
Listiterator<T> & Listiterator<T>::operator++() {
    current = current->next;
    return *this;
}

template <class T>
Listiterator<T> & Listiterator<T>::operator++(int) {
    Listiterator<T> saveList = Listiterator(this);
    current = current->next;
    return saveList;
}

3 个答案:

答案 0 :(得分:0)

这个循环是不安全的:在l.erase(it)之后,迭代器&#34;它&#34;不再有效,但循环不断尝试增加它。

for (Listiterator<int>::iterator it = l.begin(); it != l.end(); it++) {
    if (*it == 31) {
        l.erase(it);
        l.displayList();
    }
}

使其安全的一种方法是在擦除元素后中断:

for (Listiterator<int>::iterator it = l.begin(); it != l.end(); it++) {
    if (*it == 31) {
        l.erase(it);
        break;
    }
}

答案 1 :(得分:0)

我相信this是您正在寻找的答案。

它基本上解释了如何在您重复遍历列表时删除列表中的项目,而不会丢失对当前下一个迭代器的引用。

答案 2 :(得分:0)

首先,请仅将您自己的链接列表作为教育项目实施。在生产代码中,您应该在合理可能的情况下选择std::list,因为它经过了充分测试,具有深思熟虑的界面,并使您无需进行此项工作。

您可以在函数末尾相应地设置迭代器。

template <typename T> // I think, typename is less confusing than class here.
void List<T>::erase(iterator & pos) {
    Node<T>* currentNode = pos.current;
    Node<T>* nextNode = pos.current->next;
    Node<T>* searchNode = head;

    if (currentNode == nullptr) { // prefer nullptr in modern C++
        cout << "LIST EMPTY. (ERASE)" << endl;
        exit(0);
    }
    else if (nextNode == nullptr) {
        pop_back();
    }
    else {
        while (searchNode->next != currentNode &&
               // Protect yourself against false parameter passing
               searchNode != nullptr) {
            searchNode = searchNode->next;
        }
        if (searchNode == nullptr) {
            cout << "Iterator not for this list. (ERASE)" << endl;
            exit(0);
        }
        searchNode->next = nextNode;
        delete currentNode;
        listsize--;
        pos.current = nextNode;
    }
}
顺便说一句:我们真的需要所有那些临时工吗?

template <typename T>
void List<T>::erase(iterator & pos) {    
    if (pos.current == nullptr) {
        cout << "LIST EMPTY. (ERASE)" << endl;
        exit(0);
    }
    else if (pos.current->next == nullptr) {
        pop_back();
    }
    else {
        Node<T>* searchNode = head;
        while (searchNode->next != pos.current &&
               // Protect yourself against false parameter passing
               searchNode != nullptr) {
            searchNode = searchNode->next;
        }
        if (searchNode == nullptr) {
            cout << "Iterator not for this list. (ERASE)" << endl;
            exit(0);
        }
        searchNode->next = pos.current->next;
        delete pos.current;
        listsize--;
        pos.current = nextNode;
    }
}

考虑抛出异常而不是退出应用程序中间的某个地方。当没有人捕获异常时,程序也将终止,但它会让你的班级用户有机会以某种方式处理该错误。

请考虑使用std::unique_ptr作为下一个指针和头部。如果您能够始终在必要时始终正确地调用删除,则不会创建您将创建的任何代码。因此没有运行时开销,它使您的代码更简单 - 有时只是一点点,有时很多。