【问题标题】:Doubly Linked List Implementation with Pointers C++使用指针 C++ 实现双向链表
【发布时间】:2013-10-29 08:30:34
【问题描述】:

我目前正在自学 C++,并尝试使用部分完整的指针在 C++ 中实现双向链表。我知道代码当前无法处理悬空节点或输出错误,我将在接下来实现这两者。但是,代码至少应该能够构造一个列表对象并向其添加元素。目前,当我尝试调用列表的构造函数时出现错误,表示我正在请求从 LinkedList* 转换为非标量类型 LinkedList。为什么我的列表被声明为指针?任何帮助将不胜感激,谢谢!

LinkedList.h
#ifndef LINKEDLIST_H
#define LINKEDLIST_H

struct dataElement {
  int key;
  int id;
};

struct Node
{
    dataElement data;
    Node* next;
    Node* prev;
};


class LinkedList
{
public:
    /** Default constructor */
    LinkedList();
    /** Default destructor */
    virtual ~LinkedList();
    void addAtFront(int newElement);
    void addAtBack(int newElement);
    int removeTop();
    int removeBottom();
    int getTop();
    int getBottom();
    int findKey(int keyToFind);
protected:
private:
    Node* head;
    Node* tail;
    int size;
};

#endif // LINKEDLIST_H


LinkedList.cpp
#include "LinkedList.h"
#include <iostream>
#include <stdlib.h>


LinkedList::LinkedList()
{
size = 0;
}

LinkedList::~LinkedList()
{
//dtor
}

void LinkedList::addAtFront(int newElement)
{
if (size == 0)
{
    Node temp;
    temp.data.id = newElement;
    temp.data.key = 0;
    head = &temp;
    tail = &temp;
    ++size;
}
else
{
    Node temp;
    temp.data.id = newElement;
    temp.data.key = size;
    temp.next = head;
    head->prev = &temp;
    head = &temp;
    ++size;
}
}

void LinkedList::addAtBack(int newElement)
{
if (size == 0)
{
    Node temp;
    temp.data.id = newElement;
    temp.data.key = 0;
    head = &temp;
    tail = &temp;
    ++size;
}
else
{
    Node temp;
    temp.data.id = newElement;
    temp.data.key = 0;
    tail->next = &temp;
    temp.prev = tail;
    tail = &temp;
    ++size;
}
}

LinkedListTest.cpp
#include "LinkedListTest.h"
#include "LinkedList.h"

int main()
{
LinkedList list = new LinkedList();
list.addAtFront(0);
}

【问题讨论】:

  • 在处理指针时,需要使用-&gt;而不是.来获取成员函数。将主 list-&gt;addAtFront(0); 设为最后一行,看看会发生什么。

标签: c++ list pointers linked-list doubly-linked-list


【解决方案1】:

该错误意味着在某处您有一个 LinkedList 列表声明为不是指针,您为其分配了一个 new LinkedList(),它的类型为 LinkedList*(而不是 LinkedList)。应该是:

LinkedList* list = new LinkedList(); // I declare a pointer to a list
list->addAtFront(0); // I call a method on a pointer to an object

LinkedList list;
list.addAtFront(0);

它们是两种不同的类型,分配在两个不同的存储中,这很重要,请继续阅读。

我看到的更重要的是,当您使用动态分配的内存时,您应该考虑实际分配堆对象的情况,这些对象应该保持声明它们的范围。

更具体地说,这个:

{
  Node temp;
  ..
  head = &temp;
  ..
}

这将导致问题,因为temp 被声明为堆栈上的自动存储,这意味着一旦您获得它的地址并将其分配给headtail 或其他任何东西,该地址将不再有效一次范围退出。你应该在堆上分配它:

Node temp = new Node(value, id);
head = temp;
tail = temp;
++size;

请注意,当不再需要 Node 时,这需要您自己从堆中清理内存。

【讨论】:

  • 这意味着我需要在 addElement 方法之外声明节点正确吗?如果是这样,我是否需要将其视为一个数组并声明 10 个初始节点并在必要时扩展列表?我想我可以只使用添加元素方法来相应地移动前指针和尾指针。这似乎有点低效。
  • 不,不正确。您在 addElement 中声明它们,但通过 new 运算符。这将为堆上的列表元素分配内存,而不是本地分配给堆栈。在这里阅读:stackoverflow.com/questions/79923/…
【解决方案2】:

new 返回指向您试图分配给 LinkedList 对象的 LinkedList 对象的指针,而不是指针。

LinkedList list = new LinkedList();

应该阅读

LinkedList list;

【讨论】:

    【解决方案3】:

    试试这个完全实现的双向链表:

    #include <stdio.h>
    
    struct node{
    int data;
    struct node *next,*prev;
    };
    
    struct node *head=NULL;
    
    void insert(int data, int position)
    {
        struct node *newNode=malloc(sizeof(struct node));
        newNode->data=data;
    
        if(position<1)
        {
          printf("Invalid Insertion Position \n");
          return;
        }
        if(head==NULL && position==1)
        {
            newNode->next=NULL;
            newNode->prev=NULL;
            head=newNode;
        }
        else if(head==NULL && position!=1)
        {
            printf("Invalid Insertion Position \n");
        }
        else if(position==1)
        {
            newNode->next=head;
            newNode->prev=NULL;
            if(head->next!=NULL)
            {
               head->next->prev=newNode;
            }
            head=newNode;
        }
        else
        {
            int i=0;
            struct node *temp=head;
            while(temp->next!=NULL && i<position-2)
            {
                i++;
                temp=temp->next;
            }
            if(i<position-2)
            {
                printf("Invalid Insertion Position \n");
            }
            else
            {
            newNode->next=temp->next;
            temp->next=newNode;
            newNode->prev=temp;
            if(temp->next!=NULL)
            {
                temp->next->prev=newNode;
            }
            }
    
        }
    }
    
    void delete(int position)
    {
        int i=0;
        if(position<1)
        {
            printf("Invalid Position of Deletion \n");
            return;
        }
        if(head==NULL)
        {
            return;
        }
        if(position==1)
        {
            head=head->next;
            if(head!=NULL)
            {
            head->prev=NULL;
            }
        }
        else
        {
            struct node *temp=head;
            while(temp->next->next!=NULL && i<position-2)
            {
                i++;
                temp=temp->next;
            }
             if(i<position-2)
                {
                    printf("Invalid Position of Deletion \n");
                    return;
                }
            else
                {
                    temp->next=temp->next->next;
                    if(temp->next!=NULL)
                    temp->next->prev=temp;
                }
        }
    }
    
    
    
    void printlist()
    {
        if(head==NULL)
        {
            printf("Empty List!! \n");
            return;
        }
        struct node *temp=head;
    
        while(temp!=NULL)
        {
            printf("%d",temp->data);
            printf("\t");
            temp=temp->next;
        }
        printf("\n");
    }
    
    int main()
    {
        int t;
        printf("Enter number of Test Cases: \t");
        scanf("%d", &t);
        printf("\nEnter Queries in this format: \n");
        printf("For Insertion: \t I data position \n");
        printf("\tEx:\t I 25 5 \n");
        printf("For Deletion: \t D position \n");
        printf("\tEx:\t D 2 \n\n");
    
        while(t--)
        {
            char c;
            int a,b;
            printf("Enter query: \t");
            scanf("%c", &c);
            scanf("%c", &c);
    
            if(c=='I')
            {
                scanf("%d %d", &a,&b);
                insert(a,b);
            }
            else if(c=='D')
            {
               scanf("%d", &a);
               delete(a);
            }
            printlist();
        }
    
    }
    

    【讨论】:

      【解决方案4】:

      我认为你应该实现两个类:

      1. 带有标记的双向链表:Double_sentinel_list,以及
      2. 双链接节点:Double_node

      成员变量。构造函数、析构函数:

      int size() const; 
        //Returns the number of items in the list.
      bool empty() const;  
        // Returns true if the list is empty, false otherwise.
      Type front() const;  
        // Retrieves the object stored in the node pointed to by the next pointer of the head sentinel. This function throws a underflow if the list is empty. 
      Type back() const;   
        // Retrieves the object stored in the node pointed to by the previous pointer of the tail sentinel. This function throws a underflow if the list is empty.
      Double_node<Type> *head() const;  
        // Returns the head pointer.
      Double_node<Type> *tail() const;  
        // Returns the tail pointer.
      int count( Type const & ) const;  
        // Returns the number of nodes in the linked list storing a value equal to the argument. Mutators
      

      这个类有七个突变器:

      void swap( Double_sentinel_list & );  
        // The swap function swaps all the member variables of this linked list with those of the argument.
      Double_sentinel_list &operator=( Double_sentinel_list & );  
        // The assignment operator makes a copy of the argument and then swaps the member variables of this node doubly linked sentinel list those of the copy.
      void push_front( Type const & );  
        // Creates a new Double_node<Type> storing the argument, the next pointer of which is set to the next pointer of the sentinel and the previous pointer is set to point to the sentinel. Theprevious pointer of what was the first node is set to the new node.
      void push_back( Type const & );  
        //  Similar to push_front, this places a new node at the back of the list.
      Type pop_front();  
        // Delete the first non-sentinel node at the front of the linked list and the previous and next pointers of any other node (including the sentinels) within the list. Return the object stored in the node being popped. Throw an underflow exception if the list is empty.
      Type pop_back();  
        // Similar to pop_front, delete the last non-sentinel node in the list. This function throws a underflow if the list is empty.
      int erase( Type const & );  
        // Delete the first node (from the front and other than the sentinals) in the linked list that contains the object equal to the argument (use == to to test for equality with the retrieved element). Update the previous and next pointers of any other node (including possibly the sentinels) within the list. Return the number of nodes that were deleted. 
      

      【讨论】:

        【解决方案5】:

        要么

        LinkedList list;
        list.addAtFront(0);
        

        LinkedList* list = new LinkedList();
        list->addAtFront(0);
        delete list;
        

        【讨论】:

          猜你喜欢
          • 2015-09-09
          • 2012-04-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-11
          • 2020-12-20
          • 2016-06-10
          相关资源
          最近更新 更多