【问题标题】:C++ Linked List assignment: trouble with insertion and deletionC++ 链表赋值:插入和删除问题
【发布时间】:2011-10-10 18:16:00
【问题描述】:

我正在研究 C++ 中的链表实现。我正在取得进展,但无法让插入功能和删除功能正常工作。下面是 C++ 头文件中的列表对象:

#ifndef linkList_H
#define linkList_h


//
// Create an object to represent a Node in the linked list object
// (For now, the objects to be put in the list will be integers)
//
struct Node
{
    Node() : sentinel(0) {}

    int number;
    Node* next;
    Node* prev;
    Node* sentinel;
};


//
// Create an object to keep track of all parts in the list
//
class List
{
public:

    //
    // Contstructor intializes all member data
    //
    List() : m_listSize(0), m_listHead(0) {}

    //
    // methods to return size of list and list head
    //
    Node*    getListHead() const { return m_listHead; }
    unsigned getListSize() const { return m_listSize; }

    //
    // method for adding and inserting a new node to the linked list, 
    // retrieving and deleting a specified node in the list
    //
    void  addNode(int num);
    void  insertNode(Node* current);
    void  deleteNode(Node* current);

    Node* retrieveNode(unsigned position);

private:

    //
    // member data consists of an unsigned integer representing
    // the list size and a pointer to a Node object representing head
    //
    Node*    m_listHead;
    unsigned m_listSize;
};

#endif

这是实现(.cpp)文件:

#include "linkList.h"
#include <iostream>

using namespace std;


//
// Adds a new node to the linked list
//
void List::addNode(int num)
{
    Node *newNode = new Node;
    newNode->number = num;
    newNode->next = m_listHead;
    m_listHead = newNode;
    ++m_listSize;
}


//
// NOTWORKING: Inserts a node which has already been set to front
// of the list
//
void List::insertNode(Node* current)
{
        // check to see if current node already at
        // head of list
    if(current == m_listHead)
        return;

    current->next = m_listHead;

    if(m_listHead != 0)
        m_listHead->prev = current;

    m_listHead = current;
    current->prev = 0;
}


//
// NOTWORKING: Deletes a node from a specified position in linked list
//
void List::deleteNode(Node* current)
{
    current->prev->next = current->next;
    current->next->prev = current->prev;
}


//
// Retrieves a specified node from the list
//
Node* List::retrieveNode(unsigned position)
{
    if(position > (m_listSize-1) || position < 0)
    {
        cout << "Can't access node; out of list bounds";
        cout << endl;
        cout << endl;

        exit(EXIT_FAILURE);
    }

    Node* current = m_listHead;
    unsigned pos = 0;

    while(current != 0 && pos != position)
    {
        current = current->next;
        ++pos;
    }

    return current;
 }

在客户端 C++ 代码中运行一个简短的测试程序后,输出结果如下:

Number of nodes: 10

Elements in each node:
9 8 7 6 5 4 3 2 1 0

Insertion of node 5 at the list head:
4 9 8 7 6 5 4 9 8 7

Deletion of node 5 from the linked list

如您所见,插入不仅仅是将节点 5 移动到列表的头部,而是从第三个位置开始覆盖其他节点。我尝试实现的伪代码来自麻省理工学院算法书:

LIST-INSERT(L, x)
    next[x] <- head[L]
    if head[L] != NIL
        then prev[head[L]] <- x
    head[L] <- x
    prev[x] <- NIL

当调用该方法时,删除实现也会崩溃。不知道为什么;但这里是对应的伪代码:

LIST-DELET'
    next[prev[x]] <- next[x]
    prev[next[x]] <- prev[x]

说实话,我不确定前一个、下一个和哨兵指针是如何在内存中实际工作的。我知道他们在实际意义上应该做什么,但是看看调试器,在删除的情况下,这些指针似乎没有指向任何东西:

(*current).prev 0xcdcdcdcd {number=??? next=??? prev=??? ...}   Node *
    number          CXX0030: Error: expression cannot be evaluated  
    next            CXX0030: Error: expression cannot be evaluated  
    prev            CXX0030: Error: expression cannot be evaluated  
    sentinel    CXX0030: Error: expression cannot be evaluated  

任何帮助将不胜感激!

【问题讨论】:

  • 我很惊讶每个节点都有一个哨兵节点...嗯...
  • 你知道标准库中已经存在单链表和双链表了吗?
  • 我知道 STL 并喜欢使用它,但这是我的数据结构类的作业,不允许使用 STL。
  • @Dylan:那么请将作业标记为作业,以便人们知道我们在处理什么。 任何标签不如“homework”标签重要。

标签: c++ algorithm linked-list


【解决方案1】:

addNode() 中有错误。在你解决这个问题之前,你不能指望 insertNode 工作。

另外,我认为你的设计很愚蠢。例如,一个名为“insertNode”的方法应该在任意位置插入一个新项目,但是您的方法 insertNode 做了完全不同的事情,因此您应该重命名它。 addNode 也应该重命名。也正如glowcoder所写,为什么有这么多哨兵?我担心你的班级设计总体上很糟糕。

实际的错误是你忘记设置旧头的prev属性。它应该指向新的头部。

void List::addNode(int num)
{
    Node *newNode = new Node;
    newNode->number = num;
    newNode->next = m_listHead;

    if(m_listHead) m_listHead->prev = newNode;

    m_listHead = newNode;
    ++m_listSize;
}

同样,deleteNode() 中还有另一个错误。从列表中删除最后一项时它不起作用。

void List::deleteNode(Node* current)
{
    m_listSize--;
    if(current == m_listHead) m_listHead = current->next;
    if(current->prev) current->prev->next = current->next;
    if(current->next) current->next->prev = current->prev;
}

现在您可以修复所谓的 insertNode:

void List::insertNode(Node* current)
{
    int value = current->number;
    deleteNode(current);
    addNode(value);
}

请注意,我在这里编写的所有内容都没有在 C++ 编译器中进行编译和测试。也许有一些错误,但我仍然希望它至少对你有一点帮助。

【讨论】:

  • 我要补充一点,我用于插入算法的代码是我试图实现麻省理工学院算法书中描述的伪代码。
  • 我不确定哨兵。我正在阅读麻省理工学院的书,它说使用哨兵可以避免进行边界检查。我还没有弄清楚那部分代码,应该把它注释掉。
  • 另外,由于我的“所谓的”插入过程不是最好的方法,你能指出一个可以实现任意位置插入效果的算法的方向吗?谢谢。
  • Dylan,您可以创建一对方法InsertAfter(Node *existing, int newvalue)InsertBefore(Node *existing, int newvalue),它们将分别在现有节点之后或之前插入一个新节点。我希望你设法自己实现它们。
【解决方案2】:

在 deleteNode 中,您没有处理 current->next 和/或 current->prev 为空的情况。此外,如果 current 恰好是列表头,则您不会更新列表头。

你应该这样做:

node* next=current->next;
node* prev=current->prev;

if (next!=null) next->prev=prev;
if (prev!=null) prev->next=next;

if (m_listhead==current) m_list_head=next;

(警告:我还没有实际测试过上面的代码 - 但我认为它很好地说明了我的想法)

我不确定您的 InsertNode 方法究竟是做什么的,所以我无法提供任何帮助。

【讨论】:

  • 不幸的是,当调用此删除方法时,我的程序仍然崩溃,在您这里的第二个 if 语句中
【解决方案3】:

好的。

  1. 正如@Al Kepp 指出的那样,您的“添加节点”有问题。查看 Al 的代码并修复它。

  2. 您正在执行的“插入”似乎不是普通的列表插入。相反,这似乎是一个“移到前线”的行动。

  3. 尽管如此,您需要先将节点从列表中的当前位置删除,然后再将其添加到列表的开头。

更新

我认为您误解了 insert 应该如何工作。它应该插入一个新节点,而不是一个已经在列表中的节点。

请参阅下面的简单示例。

#include <iostream>

// List Node Object
//
struct Node
{
    Node(int n=0);
    int nData;
    Node* pPrev;
    Node* pNext;
};

Node::Node(int n)
: nData(n)
, pPrev(NULL)
, pNext(NULL)
{
}

//
// List object
//
class CList
{
public:

    //
    // Contstructor 
    //
    CList();

    //
    // methods to inspect list
    //
    Node*    Head() const;
    unsigned Size() const;
    Node*    Get(unsigned nPos) const;
    void     Print(std::ostream &os=std::cout) const;

    //
    // methods to modify list
    //
    void Insert(int nData);
    void Insert(Node *pNew);
    void Delete(unsigned nPos);
    void Delete(Node *pDel);

private:
    //
    // Internal data
    //
    Node*    m_pHead;
    unsigned m_nSize;
};
/////////////////////////////////////////////////////////////////////////////////
CList::CList()
: m_pHead(NULL)
, m_nSize(0)
{
}
Node *CList::Head() const 
{ 
    return m_pHead; 
}
unsigned CList::Size() const 
{ 
    return m_nSize; 
}
void CList::Insert(int nData)
{
    Insert(new Node(nData));
}
void CList::Insert(Node *pNew)
{
    pNew->pNext = m_pHead;
    if (m_pHead)
        m_pHead->pPrev = pNew;
    pNew->pPrev = NULL;
    m_pHead     = pNew;
    ++m_nSize;
}
void CList::Delete(unsigned nPos)
{
    Delete(Get(nPos));
}
void CList::Delete(Node *pDel)
{
    if (pDel == m_pHead)
    {
        // delete first
        m_pHead = pDel->pNext;
        if (m_pHead)
            m_pHead->pPrev = NULL;
    }
    else
    {
        // delete subsequent
        pDel->pPrev->pNext = pDel->pNext;
        if (pDel->pNext)
            pDel->pNext->pPrev = pDel->pPrev;
    }
    delete pDel;
    --m_nSize;
}

Node* CList::Get(unsigned nPos) const
{
    unsigned nCount(0);
    for (Node *p=m_pHead; p; p = p->pNext)
        if (nCount++ == nPos)
            return p;
    throw std::out_of_range("No such node");
}

void CList::Print(std::ostream &os) const
{
    const char szArrow[] = " --> ";

    os << szArrow;
    for (Node *p=m_pHead; p; p = p->pNext)
        os << p->nData << szArrow;
    os << "NIL\n";
}

int main()
{
    CList l;

    l.Print();

    for (int i=0; i<10; i++)
        l.Insert((i+1)*10);

    l.Print();

    l.Delete(3);

    l.Delete(7);

    l.Print();

    try
    {
        l.Delete(33);
    }
    catch(std::exception &e)
    {
        std::cerr << "Failed to delete 33: " << e.what() << '\n';
    }

    l.Print();

    return 0;
}

【讨论】:

  • 如上所述,我遵循了 MIT 书中描述的算法,其中指出 list-insert 过程将当前节点拼接到列表的前面。我宁愿按照您描述的方式实施,但我不知道该怎么做...
猜你喜欢
  • 1970-01-01
  • 2021-06-15
  • 2014-11-14
  • 2015-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-24
相关资源
最近更新 更多