双向链表模板向量中的输入排序(C++)

Posted

技术标签:

【中文标题】双向链表模板向量中的输入排序(C++)【英文标题】:Sorting Input in Vector of Doubly Linked List Template (C++) 【发布时间】:2014-03-03 02:44:39 【问题描述】:

我正在编写一个程序,该程序从一个 .txt 文件中获取输入,该文件包含一个人的名字、姓氏、9 位通用标识号 (UIN) 和他们的电话号码。我应该将此文件的输入读入双向链接列表的向量,其中向量的元素0对应于带有第一个字母'A'的姓氏的双向链接列表,元素1对应于最后的双向链接列表第一个字母“B”的名字等等。

我的代码可以从文件中读取数据并将其放入正确的列表中,但这些列表未排序。我应该在我的 TemplateDoublyLinkedList 中编写一个插入函数,它将新元素放置在列表中的正确位置,假设列表已排序,并且在我插入新元素后将保持正确排序。

我的错误不知何故来自这个函数,但我得到了非常模糊的链接器错误(这意味着我可能犯了一些愚蠢的错误),我不知道如何修复它。这是我的代码:

TemplateDoublyLinkedList.h

#include "Record.h"
#include <cstdlib>
#include <iostream>
#include <string>
#include <cstdio>
#include <sstream>
#pragma once
using namespace std;
template <typename T>
class DoublyLinkedList; // class declaration

// list node
template <typename T>
class DListNode 
private: T obj;
  DListNode<T> *prev, *next;
  friend class DoublyLinkedList<T>;
public:
  DListNode<T>(T object = T(), DListNode<T> *p = NULL, DListNode<T> *n = NULL)
    : obj(object), prev(p), next(n) 
  T getElem() const  return obj; 
  DListNode<T> * getNext() const  return next; 
  DListNode<T> * getPrev() const  return prev; 
;

// doubly linked list
template <typename T>
class DoublyLinkedList 
protected: DListNode<T> header, trailer;
public:
  DoublyLinkedList<T>() : header(T()), trailer(T()) // constructor
   header.next = &trailer; trailer.prev = &header; 
  DoublyLinkedList<T>(const DoublyLinkedList<T>& dll); // copy constructor
  ~DoublyLinkedList<T>(); // destructor
  DoublyLinkedList<T>& operator=(const DoublyLinkedList<T>& dll); // assignment operator
  // return the pointer to the first node
  DListNode<T> *getFirst() const  return header.next;  
  // return the pointer to the trailer
  const DListNode<T> *getAfterLast() const  return &trailer; 
  // return if the list is empty
  bool isEmpty() const  return header.next == &trailer; 
  T first() const; // return the first object
  T last() const; // return the last object
  void insertFirst(T newobj); // insert to the first of the list
  T removeFirst(); // remove the first node
  void insertLast(T newobj); // insert to the last of the list
  T removeLast(); // remove the last node
  DListNode<T> *insert(T& newobj);
;
// output operator
template <typename T>
ostream& operator<<(ostream& out, const DoublyLinkedList<T>& dll);

// extend range_error from <stdexcept>
struct EmptyDLinkedListException : std::range_error 
  explicit EmptyDLinkedListException(char const* msg=NULL): range_error(msg) 
;

// copy constructor
template <typename T>
DoublyLinkedList<T>::DoublyLinkedList(const DoublyLinkedList<T>& dll)

  header=T();
  trailer=T();
  header.next = &trailer; trailer.prev = &header;
  if (dll.isEmpty()) 
      return;

  DListNode<T>* current = dll.getFirst();
  while (current != dll.getAfterLast()) 
      T newObj = current->obj;
      insertLast(newObj);
      current = current->getNext();
  


// assignment operator
template <typename T>
DoublyLinkedList<T>& DoublyLinkedList<T>::operator=(const DoublyLinkedList<T>& dll)

  delete this;

  header.next = &trailer; trailer.prev = &header;

  DListNode<T>* current = dll.getFirst();
  while (current != dll.getAfterLast()) 
      T newObj = current->obj;
      insertLast(newObj);
      current = current->getNext();
  

  return *this;

// insert the object to the first of the linked list
template <typename T>
void DoublyLinkedList<T>::insertFirst(T newobj)
 
  DListNode<T> *newNode = new DListNode<T>(newobj, &header, header.next);
  header.next->prev = newNode;
  header.next = newNode;

// insert the object to the last of the linked list
template <typename T>
void DoublyLinkedList<T>::insertLast(T newobj)

  DListNode<T> *newNode = new DListNode<T>(newobj, trailer.prev,&trailer);
  trailer.prev->next = newNode;
  trailer.prev = newNode;

// remove the first object of the list
template <typename T>
T DoublyLinkedList<T>::removeFirst()
 
  if (isEmpty())
    throw EmptyDLinkedListException("Empty Doubly Linked List");
  DListNode<T> *node = header.next;
  node->next->prev = &header;
  header.next = node->next;
  T obj = node->obj;
  delete node;
  return obj;

// remove the last object of the list
template <typename T>
T DoublyLinkedList<T>::removeLast()

  if (isEmpty())
    throw EmptyDLinkedListException("Empty Doubly Linked List");
  DListNode<T> *node = trailer.prev;
  node->prev->next = &trailer;
  trailer.prev = node->prev;
  T obj = node->obj;
  delete node;
  return obj;

// destructor
template <typename T>
DoublyLinkedList<T>::~DoublyLinkedList()

  DListNode<T> *prev_node, *node = header.next;
  while (node != &trailer) 
    prev_node = node;
    node = node->next;
    delete prev_node;
  
  header.next = &trailer;
  trailer.prev = &header;

// return the first object
template <typename T>
T DoublyLinkedList<T>::first() const
 
  if (isEmpty())
    throw EmptyDLinkedListException("Empty Doubly Linked List");
  return header.next->obj;

// return the last object
template <typename T>
T DoublyLinkedList<T>::last() const

  if (isEmpty())
    throw EmptyDLinkedListException("Empty Doubly Linked List");
  return trailer.prev->obj;

// return the list length
template <typename T>
int DoublyLinkedListLength(DoublyLinkedList<T>& dll) 
  DListNode<T> *current = dll.getFirst();
  int count = 0;
  while(current != dll.getAfterLast()) 
    count++;
    current = current->getNext(); //iterate
  
  return count;

// output operator
template <typename T>
ostream& operator<<(ostream& out, const DoublyLinkedList<T>& dll) 
  DListNode<T> *current = dll.getFirst();
  while (current != dll.getAfterLast()) 
      out << current->getElem() << '\n';
      current = current->getNext();
  
  return out;


template <typename T>
DListNode<T> *insert(T& obj) 
    DListNode<T> *current = this->getFirst();
    while (obj < current->obj) 
        current = current->getNext();
    

    DListNode<T> *newNode = new DListNode<T>(obj, current->prev, current);
    curren->prev = newNode;
    added->prev->next = added;
    return newNode;

Record.h(用于我的 ADT 的类,包含每个“人员”及其姓名、UIN 和电话号码)

#include <iostream>
#include <cstdlib>
#include <string>
#pragma once
using namespace std;

class Record 
private:
    string lastName, firstName, universalIdentificationNumber, phoneNumber;
public:
    Record(string last = "EMPTY", string first = "EMPTY", string UIN = "EMPTY", string phone = "EMPTY") : 
        lastName(last), firstName(first), universalIdentificationNumber(UIN), phoneNumber(phone) 
    bool operator < (const Record& r);

    string getLast() const  return lastName; 
    string getFirst() const  return firstName; 
    string getUIN() const  return universalIdentificationNumber; 
    string getPhone() const  return phoneNumber; 

    void setLast(string s)  lastName = s; 
    void setFirst(string s)  firstName = s; 
    void setUIN(string s)  universalIdentificationNumber = s; 
    void setPhone(string s)  phoneNumber = s; 
;

ostream& operator<<(ostream& out, const Record& r);

记录.cpp

#include "Record.h"
#include "TemplateDoublyLinkedList.h"
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;

bool Record::operator < (const Record& r) 
    if (getLast().compare(r.getLast()) < 0) 
        return true;
    if (getLast().compare(r.getLast()) >0)
        return false;
    if (getLast().compare(r.getLast()) == 0) 
        if (getFirst().compare(r.getFirst()) < 0)
            return true;
        if (getFirst().compare(r.getFirst()) > 0)
            return false;
        if (getFirst().compare(r.getFirst()) == 0) 
            if (getUIN().compare(r.getUIN()) < 0) 
                return true;
            if (getUIN().compare(r.getUIN()) > 0)
                return false;
            if (getUIN().compare(r.getUIN()) == 0) 
                if (getPhone().compare(r.getPhone()) < 0)
                    return true;
                if (getPhone().compare(r.getPhone()) > 0)
                    return false;
                if (getPhone().compare(r.getPhone()) == 0)
                    return true;
            
        
    
    return false;


ostream& operator<<(ostream& out, const Record& r) 
    out << "Last Name: " << r.getLast() << '\n'
        << "First Name: " << r.getFirst() << '\n'
        << "UIN: " << r.getUIN() << '\n'
        << "Phone Number: " << r.getPhone() << '\n';

    return out;

Main.cpp

#include "Record.h"
#include "TemplateDoublyLinkedList.h"
#include <cstdlib>
#include <string>
#include <iostream>
#include <cstdio>
#include <sstream>
#include <vector>
#include <fstream>
using namespace std;

void display(vector<DoublyLinkedList<Record>>& v) 
    for (int i=0; i<v.size(); ++i) 
        cout << v[i];
    


int firstLetterSort (string s) 
    char firstLetter = s.at(0);
    int location;

    switch (firstLetter) 
    case 'a':
    case 'A':
        location = 0;
        break;
    case 'b':
    case 'B':
        location = 1;
        break;
    case 'c':
    case 'C':
        location = 2;
        break;
    case 'd':
    case 'D':
        location = 3;
        break;
    case 'e':
    case 'E':
        location = 4;
        break;
    case 'f':
    case 'F':
        location = 5;
        break;
    case 'g':
    case 'G':
        location = 6;
        break;
    case 'h':
    case 'H':
        location = 7;
        break;
    case 'i':
    case 'I':
        location = 8;
        break;
    case 'j':
    case 'J':
        location = 9;
        break;
    case 'k':
    case 'K':
        location = 10;
        break;
    case 'l':
    case 'L':
        location = 11;
        break;
    case 'm':
    case 'M':
        location = 12;
        break;
    case 'n':
    case 'N':
        location = 13;
        break;
    case 'o':
    case 'O':
        location = 14;
        break;
    case 'p':
    case 'P':
        location = 15;
        break;
    case 'q':
    case 'Q':
        location = 16;
        break;
    case 'r':
    case 'R':
        location = 17;
        break;
    case 's':
    case 'S':
        location = 18;
        break;
    case 't':
    case 'T':
        location = 19;
        break;
    case 'u':
    case 'U':
        location = 20;
        break;
    case 'v':
    case 'V':
        location = 21;
        break;
    case 'w':
    case 'W':
        location = 22;
        break;
    case 'x':
    case 'X':
        location = 23;
        break;
    case 'y':
    case 'Y':
        location = 24;
        break;
    case 'z':
    case 'Z':
        location = 25;
    
    return location;


int main() 
    vector<DoublyLinkedList<Record>> phoneBook(26);
    const string phoneBookFile = "PhoneBook";
    string searchedLast, searchedFirst, searchedUIN;
    int firstLetter;

    ifstream ist(phoneBookFile.c_str());
    ist.open("PhoneBook.txt");
    if (ist.is_open()) 
        while (!ist.eof()) 
            Record nextRecord;
            string first, last, UIN, phone, empty;

            getline(ist, last);
            getline(ist, first);
            getline(ist, UIN);
            getline(ist, phone);
            getline(ist, empty);

            nextRecord.setLast(last);
            nextRecord.setFirst(first);
            nextRecord.setUIN(UIN);
            nextRecord.setPhone(phone);

            int location = firstLetterSort(last);
            phoneBook[location].insert(nextRecord);
        
    

    display(phoneBook);

还有我的编译器输出的错误:

1>Main.obj : error LNK2019: unresolved external symbol "public: class DListNode<class Record> * __thiscall DoublyLinkedList<class Record>::insert(class Record &)" (?insert@?$DoublyLinkedList@VRecord@@@@QAEPAV?$DListNode@VRecord@@@@AAVRecord@@@Z) referenced in function _main
1>C:\Users\Snyperanihilatr\Documents\Visual Studio 2012\Projects\Phonebook\Debug\Phonebook.exe : fatal error LNK1120: 1 unresolved externals

【问题讨论】:

我一看到这个就停止阅读:delete this;,然后是一堆被引用的成员变量。编译器错误只是冰山一角。我无法想象如果不对其中的一些进行单元测试,可以编写多少代码。 唯一不起作用的是 templatedoublylinkedlist.h 文件中的插入函数 我一路测试了其他所有内容,一切正常,甚至当我不调用该函数时它也可以工作插入。但是,当我在 main 中调用函数 insert 时,出现链接错误。 我不同意这一点。甚至读取循环也错误地使用std::istream::eof() 作为条件并且未能测试所有包含的提取是否成功。如果您想知道为什么您的 last 记录被插入 两次,这就是原因。我原来的评论是成立的。 delete this; 后跟 this 的引用成员会调用清晰呈现的未定义行为 【参考方案1】:

很简单,复制构造函数没有在 DoubleLinkedList 中定义主体。通常这样做是为了防止它被使用。正如您在下面看到的,复制构造函数没有主体,这就是链接器找不到它的原因。

DoublyLinkedList<T>(const DoublyLinkedList<T>& dll); // copy constructor

您需要复制构造函数的原因是向量模板使用它。您可以通过存储指向 DoubleLinkedList 而不是对象的指针来防止这种情况发生。

vector<DoublyLinkedList<Record>*> phoneBook(26);
for(char c='a' ; c<='z'; c++) 

    phoneBook[c] = new DoubleLinkedList();

【讨论】:

没有定义是什么意思?我在我的班级中声明了它,然后它是在我的班级之外定义的第一个函数。我应该怎么做才能解决它? 好的,它已经定义但没有实现。它没有实体。

以上是关于双向链表模板向量中的输入排序(C++)的主要内容,如果未能解决你的问题,请参考以下文章

二叉搜索树与双向链表(Python and C++版本)

双向链表排序c语言程序设计

双向链表排序c语言程序设计

代码模板实现双向链表的去重拼接合并排序

如何在双向链表 C++ 上使用插入排序?

动态分配的双向链表类实例 segfault C++