【问题标题】:Logic Error With Function to Manipulate Linked List处理链表的函数的逻辑错误
【发布时间】:2018-03-31 20:06:24
【问题描述】:

我需要帮助确定我的逻辑错误在哪里。我正在用 C++ 编写一个函数,它以单链表作为输入,旨在删除奇数节点,同时复制偶数节点:

示例:

initial: 0  5  2  2  7  3  7  9  0  2
 ought2b: 0  0  2  2  2  2  0  0  2  2  
outcome: 0  0  2  2  2  2  0  0  2  2  

initial: 0
 ought2b: 0  0

initial: 1
 ought2b: (empty)

我遇到的问题是我的结果是正确的(如上所示),但是在第二个链表之后,整个程序退出,当我只能假设是分段错误时,我得到了一个退出代码 11。我已经重写了整个函数 6 次以上,但在精神上找不到我在哪里犯了逻辑错误,因为我的代码通常最终看起来与每次迭代都相似。任何帮助将不胜感激!函数代码如下所示。

void RemOddDupEven(Node*& headPtr){
    // Define two cursors to iterate through referenced list.
    Node *precursor = 0,
            *cursor = headPtr;

    // Error Checking | Check for reference to empty list.
    if(headPtr == 0){
        cerr << "RemOddDupEven() attempted on empty list" << endl;
        return;
    }

    while(cursor->link != 0){
        if(cursor->data%2 != 0){
            /// Odd node found in referenced list. Remove it.
            if(precursor == 0){
                // No previous. Removing first node.
                precursor = cursor;
                cursor = cursor->link;
                headPtr = cursor;
                delete precursor;
                precursor = 0;
            } else {
                Node* tempNodePtr = cursor;
                cursor = cursor->link;
                precursor->link = cursor;
                delete tempNodePtr;
            }
        } else {
            /// Even node found in referenced list. Duplicate it.
            if(precursor == 0){
                // No previous. Duplicate and insert before current node.
                Node* newNodePtr = new Node;
                newNodePtr->data = cursor->data;
                newNodePtr->link = cursor;
                headPtr = newNodePtr;
                precursor = cursor;
                cursor = cursor->link;
            } else {
                // There is a previous. Duplicate and insert before current.
                Node* newNodePtr = new Node;
                newNodePtr->data = cursor->data;
                precursor->link = newNodePtr;
                newNodePtr->link = cursor;
                precursor = cursor;
                cursor = cursor->link;
            }
        }

    }
    /// We are at last item in the list.
    if(cursor->data%2 != 0){
        /// Odd node found at end of referenced list. Remove it.
        precursor->link = 0;
        delete cursor;
        cursor = 0;
    } else {
        /// Even node found at the end of referenced list. Duplicte it.
        Node* newNodePtr = new Node;
        newNodePtr->data = cursor->data;
        precursor->link = newNodePtr;
        newNodePtr->link = cursor;
    }
}

【问题讨论】:

  • 整个函数我已经重写了 6 次以上 你真的需要一个能够调试的 IDE。你不应该重写你的代码来猜测你错误地实现了什么。
  • 您需要做的第一件事是使用调试器单步执行代码并找出程序结束的行。然后您可以创建一个包含该行并重现该行为的minimal reproducible example

标签: c++ linked-list segmentation-fault singly-linked-list


【解决方案1】:

你的函数太复杂了,因为函数内部有很多特殊条件,所以很难排查。

但乍一看很明显,例如这段代码 sn-p

/// We are at last item in the list.
if(cursor->data%2 != 0){
    /// Odd node found at end of referenced list. Remove it.
    precursor->link = 0;
    delete cursor;
    cursor = 0;
} else {

错了。

让我们考虑一个只有一个节点是头节点的列表。

在这种情况下是循环

while(cursor->link != 0){
    //...
}

不会被执行,因为headPtr-&gt;link 等于nullptr。这也意味着precursor 等于0(在循环之前它最初设置为0)。所以表达式语句

precursor->link = 0;

导致函数的未定义行为。

同时声明

    cursor = 0;

不会更改headPtr 的值,因为cursorheadPtr 是两个不同的对象,更改一个对象的值不会影响另一个对象的值。

函数实现看起来更简单。

这是一个演示程序

#include <iostream>

struct Node
{
    int data;
    Node *link;
};

void initialize(Node * &head, const int a[], size_t n)
{
    Node **current = &head;

    while (*current) current = &(*current)->link;

    for (size_t i = 0; i < n; i++)
    {
        *current = new Node{ a[i], nullptr };
        current = &(*current)->link;
    }
}

void RemOddDupEven(Node * &head)
{
    Node **current = &head;

    while (*current)
    {
        if ((*current)->data % 2 == 0)
        {
            (*current)->link = new Node{ (*current)->data, (*current)->link };
            current = &(*current)->link->link;
        }
        else
        {
            Node *tmp = *current;
            *current = (*current)->link;
            delete tmp;
        }
    }
}

std::ostream & show(Node * &head, std::ostream &os = std::cout)
{
    for (Node *current = head; current; current = current->link)
    {
        os << current->data << ' ';
    }

    return os;
}

int main()
{
    Node *head = nullptr;
    int a[] = { 0, 5, 2, 2, 7, 3, 7, 9, 0, 2 };

    initialize(head, a, sizeof(a) / sizeof(*a));

    show(head) << std::endl;

    RemOddDupEven(head);

    show(head) << std::endl;

    return 0;
}

它的输出是

0 5 2 2 7 3 7 9 0 2
0 0 2 2 2 2 0 0 2 2

【讨论】:

  • 弗拉德,谢谢!我非常感谢您的解释。您能否详细说明使用指向指针的指针背后的原因?节点 **current = &head;
  • @AlexanderMaxwell 实际上 cursor 是间接的 C 含义中对 head 的引用。使用指向头的指针,我们可以通过指针改变头。这与在这样的函数中的方式相同 void f( int * p ) { * p = 10; } /*...*/ int x = 5; f(&x); std::cout
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-03
  • 1970-01-01
  • 2022-01-10
  • 2011-09-28
  • 2015-06-01
相关资源
最近更新 更多