c++链表赋值:插入和删除的麻烦

C++ Linked List assignment: trouble with insertion and deletion

本文关键字:删除 麻烦 插入 链表 赋值 c++      更新时间:2023-10-16

我正在研究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  

任何帮助将非常感激!!

您在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++编译器中编译和测试。可能会有一些错误,但我仍然希望它至少对你有一点帮助。

在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方法到底是什么,所以我不能在那里提供任何帮助。

OK。

  1. 正如@Al Kepp指出的,你的"添加节点"有bug。

  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 << "NILn";
}
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;
}