【问题标题】:C++ - How to subtract two Linked Lists from one another?C++ - 如何将两个链表相减?
【发布时间】:2021-09-03 05:36:09
【问题描述】:

我试图找到一种方法来获取两个链接列表对象(双向链接),比如 LL1 和 LL2,并删除 LL1 中也出现在 LL2 中的“重叠”元素。 例如,如果:

LL1 = {1,2,3};
LL2 = {9,2,8};

我想要一个输出:

LL1 = {1,3};

我试图通过重载“-=”运算符来处理两个链表对象来做到这一点。它编译得很好,但是我在运行时在“-=”运算符调用时遇到了“分段错误(核心转储)”错误。不知道为什么。下面是我的代码。任何帮助将不胜感激,谢谢。

节点.h:

// Node.h
/*******************************/
// Last Updated: Tues Aug 31 2021
// Program Description: Header file for Node

#ifndef NODE_H
#define NODE_H
#include <iostream>
#include <cstdlib>
#include "EToll.h"

using namespace std;

class Node {
    
    public:
        typedef EToll value_type; //typedef - now value_type is synonym for EToll object
        
        //Constructors:
        Node(); //default
        Node(value_type&); //constructor with 1 arg - data item
        Node(const value_type& i, Node* n, Node* p); //specific constructor with 3 arguments
        
        //Destructor:
        ~Node();
        
        //mutators (setters):
        void set_next(Node*);
        void set_prev(Node*);
        void set_data(const value_type&);
        
        //accessors (getters):
        Node* get_next() const;
        Node* get_prev() const;
        value_type& get_data();
        
    private:
        Node* next; //ptr to next (or NULL)
        Node* prev; //ptr to prev (or NULL)
        value_type data; //the payload

};

#endif

Node.cpp:

// Node.cpp
/*******************************/
// Last Updated: Tues Aug 31 2021
// Program Description: Implementation of Node

#include "Node.h"
#include <cstdlib>

//Constructors:
Node::Node() //default constructor
{
    data = value_type(); //creates an empty EToll object, since value_type is a synonym for EToll
    next = NULL;
    prev = NULL;
}

Node::Node(value_type& item) //constructor with 1 argument - a data item
{
    data = item;
    next = NULL;
    prev = NULL;
}

Node::Node(const value_type& i, Node* n, Node* p) //constructor with 3 arguments
{
    data = i;
    next = n;
    prev = p;
}

//Empty destructor:
Node::~Node(){}

//Mutators (setters):
void Node::set_next(Node* next_ptr) {next = next_ptr;}
void Node::set_prev(Node* prev_ptr) {prev = prev_ptr;}
void Node::set_data(const value_type& new_data) {data = new_data;}

//Accessors (getters):
Node* Node::get_next() const {return next;}
Node* Node::get_prev() const {return prev;}
/* Note that get_data() has Node::value_type& instead of value_type& as it's return type as the 
compiler doesn't check if the return type of a function is part of a member function, and thus
it doesn't look in Node for a value_type. A more detailed explanation can be found at: https://stackoverflow.com/questions/68991650/error-value-type-does-not-name-a-type-in-c */
Node::value_type& Node::get_data() {return data;}

LinkedList.h:

// LinkedList.h
/*******************************/
// Last Updated: Tues Aug 31 2021
// Program Description: Header file for LinkedList

#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include "Node.h"
#include <iostream>
#include <cstdlib>
#include <string>

class LinkedList 
{
public:
    typedef Node::value_type value_type;

    //Constructor:
    LinkedList();
    
    //Destructor:
    ~LinkedList();
    
    //Insert function:
    //void insert(value_type& item);
    
    //length function:
    int length();
    
    //count function:
    int count(string);
    
    //totalIncome function:
    double totalIncome();
    
    //addTo functions:
    void addToHead(value_type&);
    void addToTail(value_type&);
    
    //accessors (getters):
    value_type& getHead();
    Node* getHeadAdd();
    value_type& getTail();
    
    //remove functions:
    void removeFromHead();
    void removeFromTail();
    void remove(string);
    void removeByNode(Node* c); //remove a particular node
    
    //search funciton:
    //Preconditions: None
    //Postconditions: Current points to the first Node storing the target, and true is returned. If not present, current is NULL and false is returned
    bool search(const value_type& target);
    
    //concatenation operator (+=) overload:
    //Preconditions: LL1 and LL2 are instances of LinkedList
    //Postconditions: Each Node of LL2 is traversed. At each individual Node, itis appended to LL1
    LinkedList operator += (const LinkedList& LL2);
    
    //remove overlap operator (-=) overload:
    //Preconditions: LL1 and LL2 are instances of LinkedList
    //Postconditions: Each Node of LL2 is traversed. At each individual Node, it's match is searched for within LL1. If a match is found, the matching Node in LL1 is deleted and the next node in LL2 is traversed
    LinkedList operator -= (const LinkedList& LL2);
    
    //NEED A .COUNT FUNCTION!!
    
        
private:
    Node* head;
    Node* tail;
    Node* current;
};

//stream insertion operator (<<) overload:
    //Preconditions: LinkedList obj "LL" exists and we wish to output it
    //Postconditions: LL exists without change
    ostream& operator << (ostream& out, LinkedList& LL);

#endif

LinkedList.cpp:

// LinkedList.cpp
/*******************************/
// Last Updated: Wed Aug 31 2021
// Program Description: Implementation of LinkedList

#include "LinkedList.h"
#include <cstdlib>

//Constructors:
LinkedList::LinkedList() //default constructor
{
    head = NULL;
    tail = NULL;
    current = NULL;
}

//Empty destructor:
LinkedList::~LinkedList(){}

//length() function:
//Preconditions: None
//Postconditions: A count of the nodes is returned (ie no of nodes in the LinkedList)
int LinkedList::length() 
{
    int answer = 0;
    for (current = head; current != NULL; current = current->get_next()) 
    {
        answer++;
    }
    return answer;
}

//count function:
int LinkedList::count(string type) 
{
    int returnCount = 0;
    
    //cycle through LinkedList:
    for (current = head; current != NULL; current = current->get_next()) 
    {
        //check for match:
        if (type == current->get_data().get_type())
        {
            //increment the counter
            returnCount++;
        }
    }
    
    return returnCount;
}

//totalIncome function:
double LinkedList::totalIncome() 
{
    double returnTotal = 0;
    
    //cycle through LinkedList:
    for (current = head; current != NULL; current = current->get_next()) 
    {
        returnTotal = returnTotal + current->get_data().get_charge();
    }
    
    return returnTotal;
}

//addToHead function:
//Preconditions: None
//Postconditions: A new node storing the supplied item is created and linked in to be list's new head
void LinkedList::addToHead(Node::value_type& item) 
{
    Node* newNode = new Node(item);
    
    //Check if the list is empty:
    if (length() == 0) 
    { //list is empty, so:
        head = newNode;
        tail = newNode;
    } else 
    { //list is not empty, so:
        head->set_prev(newNode);
        newNode->set_next(head);
        head = newNode;
    }
    
    /*
    head = new Node(item, head, NULL);
    //In case the list is empty:
    if (tail == NULL) 
    {
        tail = head;
    }
    */
}

//addToTail function:
//Preconditions: None
//Postconditions: A new node storing the supplied item is created and linked in to be list's new tail
void LinkedList::addToTail(Node::value_type& item) 
{
    Node* newNode = new Node(item);
    
    //Check if the list is empty:
    if (length() == 0) 
    { //list is empty, so:
        head = newNode;
        tail = newNode;
    } else 
    { //list is not empty, so:
        tail->set_next(newNode);
        newNode->set_prev(tail);
        tail = newNode;
    }
}

//getHead function:
Node::value_type& LinkedList::getHead() 
{
    return head->get_data();
}

//getHeadAdd function:
Node* LinkedList::getHeadAdd() 
{
    return head->get_next()->get_prev();
}

//getTail function:
Node::value_type& LinkedList::getTail() 
{
    return tail->get_data();
}

//removeFromHead function:
void LinkedList::removeFromHead() 
{
    Node* temp;
    temp = head->get_next();
    if (head != NULL) 
    {
        temp->set_prev(NULL);
        head = temp;
    } else 
    { //list is empty, so update the tail
        tail = NULL;
    }
}

//removeFromTail function:
void LinkedList::removeFromTail() 
{
    Node* temp;
    temp = tail->get_prev();
    if (head != NULL) 
    {
        temp->set_next(NULL);
        tail = temp;
    } else 
    { //list is empty, so update the head
        head = NULL;
    }
}

//remove function: removes a Node by a string input
void LinkedList::remove(string l) 
{
    //cycle through LinkedList:
    for (current = head; current != NULL; current = current->get_next()) 
    {
        //check for match:
        if (l == current->get_data().get_licence() && current == head) 
        {
            removeFromHead();
        } else if (l == current->get_data().get_licence() && current == tail) 
        {
            removeFromTail();
        } else if (l == current->get_data().get_licence()) 
        {
            //delete the node
            removeByNode(current);
        } else
        {
            //do nothing, move on to next iteration of for loop
        }
    }
}

//removeByNode function:
//Preconditions: input c points to a node to be removed
//Postconditions: the node pointed to by c before is now gone. current now points to head
void LinkedList::removeByNode(Node* c)
{
    current = c;
    current->get_prev()->set_next(current->get_next());
    current->get_next()->set_prev(current->get_prev());
    delete current;
    current = head;
}

//search function:
bool LinkedList::search(const Node::value_type& target)
{
    for (current = head; current != NULL; current = current->get_next()) 
    {
        if (target == current->get_data())
        {
            return true;
        }
    }
    //else:
    return false;
}

// += operator   overload (new):
LinkedList LinkedList::operator += (const LinkedList& LL2) 
{
    LinkedList* t = this;
    Node* temp = LL2.head;
    
    while (temp != NULL) 
    {
        t->addToTail(temp->get_data());
        temp = temp->get_next();
    }
    
    return *t;
}

// -= operator overload:
LinkedList LinkedList::operator -= (const LinkedList& LL2) 
{
    LinkedList* t = this;
    Node* temp1;
    Node* temp2;
    
    //Cycle through LL2:
    for (temp2 = LL2.head; temp2 != NULL; temp2 = temp2->get_next()) 
    {
        //Cycle through LL1:
        for (temp1 = t->head; temp1 != NULL; temp1 = temp1->get_next()) 
        {
            //Check if current of LL1 has a match in LL2:
            if (temp1->get_data() == temp2->get_data()) 
            {
                t->removeByNode(temp1);
            }
        }
    }
    
    return *t;
}

-= 运算符重载(在 LinkedList.cpp 中,只需将其放在新标题下以便于定位):

// -= operator overload:
LinkedList LinkedList::operator -= (const LinkedList& LL2) 
{
    LinkedList* t = this;
    Node* temp1;
    Node* temp2;
    
    //Cycle through LL2:
    for (temp2 = LL2.head; temp2 != NULL; temp2 = temp2->get_next()) 
    {
        //Cycle through LL1:
        for (temp1 = t->head; temp1 != NULL; temp1 = temp1->get_next()) 
        {
            //Check if current of LL1 has a match in LL2:
            if (temp1->get_data() == temp2->get_data()) 
            {
                t->removeByNode(temp1);
            }
        }
    }
    
    return *t;
}

-= 在另一个 'main()' 程序中调用运算符:

LinkedList tollBooth1;
LinkedList tollBooth2;
LinkedList dailyReport;

//(add data to tollBooth 1 and 2, merge these 2 objects into dailyReport)

//removing the contents of both booths from daily report:
dailyReport -= tollBooth1;
dailyReport -= tollBooth2;

【问题讨论】:

  • 你为什么不使用标准的C++ containers
  • 您是否尝试在调试器中捕捉崩溃以准确地定位它发生的位置?
  • 或者可能使用调试器在监视变量及其值的同时逐语句逐句执行代码?同时用笔和纸画出所有操作,用方框表示节点,用箭头表示所有指针。在修改指针时绘制和重绘箭头。这一切在你的绘画中都有意义吗?所有的箭头都指向有效的东西吗?
  • 看起来removeByNode 删除了temp1 然后temp1 = temp1-&gt;get_next() 在此之后使用它。
  • t compiles fine -- 如果只需要程序编译好,就不会出现 bug。编译好只意味着没有语法错误——它与程序是否逻辑正确无关。还有what is a debugger?.

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


【解决方案1】:

您包含的代码太多了。本网站的一般要求是提供一个最小的可重现示例。

问题

  1. 我不知道为什么你有一个名为current 的成员变量。它通常指向已删除的内存。

  2. 您需要在 LinkedList 类上实现“三规则”或“五规则”。如果复制 LinkedList,然后对其进行修改,则会出现悬空指针。

    LinkedList a;
    LinkedList b = a;
    a.RemoveHead();
    // b.head is now pointing to deleted memory.
    
  3. LinkedList::removeByNode(Node* c) 不更新头和尾成员变量,如果删除的节点分别是头或尾。

  4. 您的operator-= 在被删除后使用temp1

可能还有更多问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-31
    • 2015-03-22
    • 1970-01-01
    • 2014-10-04
    • 2019-01-08
    相关资源
    最近更新 更多