【问题标题】:How do you search for a specific string in a linked list and return that value?如何在链表中搜索特定字符串并返回该值?
【发布时间】:2021-01-19 05:28:27
【问题描述】:

我有一个程序可以输入 5 个参数。输入是视频标题、网址、评论、长度和评分。然后根据标题对它们进行排序。用户需要指定 insert(输入视频信息)、lookup(按标题查找视频并仅打印该视频及其相关信息)、或 print(只需简单地打印所有内容)。

例如

输入:

insert
Arthur Benjamin: Lightning calculation and other "Mathemagic"
http://www.youtube.com/watch?v=M4vqr3_ROIk
Hard to believe.
15.25  
4  
lookup  
Arthur Benjamin: Lightning calculation and other "Mathemagic"

输出:

Arthur Benjamin: Lightning calculation and other "Mathemagic" , http://www.youtube.com/watch?v=M4vqr3_ROIk, Hard to believe., 15.25, 4

我的问题是处理 lookup in main

if(user == "lookup")
        {
            getline(cin, title);
            if(vlistObj -> lookup(videoObj))
            {
                vlistObj->print();
            }
        }

以及在我的链接列表中查找

bool Vlist::lookup(Video *other)
{
  Node *node = m_head;
  return node->m_next -> m_video->GetTitle() == other-> GetTitle();
}

老实说,我对如何让 lookup 搜索特定标题(假设已经给出了很多视频标题/信息)并只打印我要求的内容(假设它在列表中)感到非常迷茫.

完整代码如下:

#include <iostream>
#include <stdlib.h>
#include <cstring>
using namespace std;

class Video {

  public:
    Video(string video_title, string video_link, string video_comment, double video_length, int video_number);
    void print();
    const string& GetTitle() const { return title; }

  private:

    std::string title;
    string link;
    string comment;
    double length;
    int rating;

};


Video::Video(string video_title, string video_link, string video_comment, double video_length, int video_number)
  : title(video_title), link(video_link), comment(video_comment), length(video_length), rating(video_number)
{
}

void Video::print(){

  cout << title << ", " << link << ", " << comment << ", " << length << ", " << rating << endl;

}


class Vlist {
 public:
  Vlist() {m_head = nullptr; }
  bool lookup(Video *other);
  void Insert(Video *video);
  void print();
 private:
 class Node {
        public:
                Node(Video *video, Node *next) {m_video = video; m_next = next; }
                Video *m_video;
                Node *m_next;
            };
            Node *m_head;
 };


void Vlist::Insert(Video* video)
{
    if (m_head == NULL || m_head->m_video -> GetTitle() > video->GetTitle())
    {
        m_head = new Node(video, m_head);

    }
    else
    {
        Node *node = m_head;
        while (node->m_next != NULL && node->m_next -> m_video->GetTitle() < video->GetTitle())
        {
            node = node->m_next;
        }
        node->m_next = new Node(video, node->m_next);
    }
 }

bool Vlist::lookup(Video *other)
{
  Node *node = m_head;
  return node->m_next -> m_video->GetTitle() == other-> GetTitle();
}

 void Vlist::print()
 {
     Video *video;
     Node *node = m_head;

     while(node != NULL)
     {
        node -> m_video-> Video::print();
        node = node->m_next;
     }
 }


int main()
{
  string sort_type, url, comment, title, user;
    int rating;
    double length;
    int initial = 0, last = 0, number;

    Vlist *vlistObj= new Vlist();
    Video *videoObj;

    while (getline(cin,user)) {

        if(user == "insert")
        {
        getline(cin,title);
        getline(cin, url);
        getline(cin, comment);
        cin >> length;
        cin >> rating;
        cin.ignore();

        videoObj = new Video(title,url, comment, length, rating);
        vlistObj->Insert(videoObj);
        }

        if(user == "lookup")
        {
            getline(cin, title);
            if(vlistObj -> lookup(videoObj))
            {
                vlistObj->print();
            }
        }
        if(user == "print")
        {
            vlistObj->print();
        }
    }

}

我还想指出,我收到了分段错误。但我确实知道这是因为我在查找中的代码。如果我不键入 lookup

,程序会正确运行并输出

【问题讨论】:

  • if (user == "lookup") ... 为什么是变量user 而不是functionaction 之类的?这是我对你想做的事情的第一个心理障碍。您可以将其分为两个问题:1. 您希望单词“lookup”触发查找功能,以及 2.您希望查找功能实际查找项目。如果其中一个已经有效,请将其从您的问题中删除。它只会使水变得浑浊。
  • 不要给出“完整代码”。给minimal reproducible example。提供其中之一有两件事:它使您的问题的读者更容易了解正在发生的事情,并且通常制作minimal reproducible example 的做法会使您更好地理解问题。当人们制作minimal reproducible examples 时,有一半的时间是他们解决了自己的问题。
  • 进入你的调试器。告诉我当你进入lookup函数时other的值是多少。
  • @JohnFilleau 哦,好吧,我不知道人们是否会在不包括所有内容的情况下理解我的问题。注意到
  • @JohnFilleau 老实说,我对编程很陌生。如何进入调试器进行检查?我的教授说我们可以使用随机指针变量来检查 Title 是否会返回 true。这就是other 所做的一切

标签: c++ class linked-list


【解决方案1】:

概述

在查看具体问题之前,您的代码表明您正在努力将所有部分组合在一起,并且从某种意义上说,您是在猜测而不是特别注意代码中的每一行。你不能仅仅通过“试试看它是否有效”来编码,那只会让你变老、灰头土脸和沮丧。花点时间确切地知道你的下一行代码需要做什么,设计该行,然后设计一个测试以确保它成功(或与gdb 交朋友并在那里检查——你表明你正在使用 g++)

例子:

#include <iostream>
#include <limits>
// #include <stdlib.h>
// #include <cstring>

stdlib.hcstring 的用途是什么?并且:

void Vlist::print()
{
    // Video *video;        /* unused */

    // int initial = 0, last = 0, number = 0;   /* unused */

other 是如何初始化的?如果您传递一个指向 Video 对象的指针,该对象必须至少初始化 title,所以 GetTitle() 返回一个有意义的值...

bool Vlist::lookup(Video *other)
{
  Node *node = m_head;
  return node->m_next -> m_video->GetTitle() == other-> GetTitle();
}

这真的没有多大意义?

慢慢来,了解自己需要做什么,然后拿起键盘(而不是反过来)

与您使用的语法保持一致。你在一些地方包含std::string,然后在其他依赖using namespace std;的地方简单地包含string。见Why is “using namespace std;” considered bad practice?

此外,在 C++ 中,-&gt; 周围永远不会有 空格。这是一个连接对象及其成员的运算符,中间没有任何内容。

具体问题

很明显,当您尝试打印Vlist 时,您所拥有的不会输出与查找匹配的Video 对象。 (打印整个列表以响应找到一个感兴趣的标题没有多大意义)。您的lookup() 函数不能返回bool,而是必须返回指向包含标题(如果找到)或nullptr(如果未找到)的节点的指针。这意味着您必须保存并验证退货,才能知道哪个Node 包含您要打印的记录。在main() 中看起来像:

        else if (user == "lookup") {
            if (getline(std::cin, title)) {
                videoObj = new Video (title);   /* you must construct a new videoObj */
                Video *video = nullptr;         /* you want a Video* pointer returned */
                if ((video = vlistObj->lookup(videoObj))) { /* lookup & assign return */
                    video->print();             /* output the video, not list */
                }
                else {
                    std::cout << "title not found: '" << title << "'.\n";
                }
            }
        }

现在videoObj 指向一个已初始化titleVideo 对象,lookup() 函数可以完成它的工作——返回一个指向包含该标题的列表中的节点的指针(以及所有其余信息)或返回 nullptr 如果找不到标题。 (注意else 向用户表明该条件)

lookup() 的重写就是:

Video *Vlist::lookup (Video *other)
{
    Node *node = m_head;
    
    while (node) {  /* iterate over nodes in list looking for title */
        if (node->m_video->GetTitle() == other->GetTitle())
            return node->m_video;           /* return pointer to node if found */
        node = node->m_next;
    }
    return nullptr;                         /* nullptr if not */
}

注意:简单地将other 传递为std::string 更有意义,但如果您需要Video 对象——这是一种最简单的方法)

还需要进行一连串的其他清理工作、对初始化的调整、语法修复——删除-&gt; 周围的空格等等……不胜枚举。更不用说需要确保您没有泄漏内存——这留给您(以及您正确编写的析构函数)使用valgrind 来验证您是否在程序退出之前释放了所有内存。

将语法清理和初始化放在一起,您可以执行以下操作:

#include <iostream>
#include <limits>

class Video {

  public:
    Video ( std::string video_title, std::string video_link, std::string video_comment, 
            double video_length, int video_number );
    void print();
    const std::string& GetTitle() const { return title; }

  private:
    std::string title {}, link {}, comment {};
    double length;
    int rating;
};


Video::Video ( std::string video_title = "", 
                std::string video_link = "", 
                std::string video_comment = "", 
                double video_length = 0, int video_number = 0)
                : title(video_title), link(video_link), comment(video_comment), 
                length(video_length), rating(video_number)
{
}

void Video::print()
{
    std::cout << title << ", " << link << ", " << comment << ", " << 
                length << ", " << rating << '\n';
}


class Vlist {
    
  public:
    Vlist() { m_head = nullptr; }
    Video *lookup (Video *other);
    void Insert (Video *video);
    void print();
  
  private:
    class Node {
      public:
        Node (Video *video = nullptr, Node *next = nullptr) {
            m_video = video; m_next = next;
        }
        Video *m_video;
        Node *m_next;
    };
    Node *m_head;
};

void Vlist::Insert (Video* video)
{
    if (m_head == nullptr || m_head->m_video->GetTitle() > video->GetTitle()) {
        m_head = new Node (video, m_head);
    }
    else {
        Node *node = m_head;
        while (node->m_next != nullptr &&
                node->m_next->m_video->GetTitle() < video->GetTitle()) {
            node = node->m_next;
        }
        node->m_next = new Node(video, node->m_next);
    }
 }

Video *Vlist::lookup (Video *other)
{
    Node *node = m_head;
    
    while (node) {  /* iterate over nodes in list looking for title */
        if (node->m_video->GetTitle() == other->GetTitle())
            return node->m_video;           /* return pointer to node if found */
        node = node->m_next;
    }
    return nullptr;                         /* nullptr if not */
}

void Vlist::print()
{
    Node *node = m_head;

    while (node != nullptr) {
        node->m_video->Video::print();
        node = node->m_next;
    }
}


int main (void) {
    
    std::string sort_type {}, url {}, comment {}, title {}, user {};
    int rating = 0;
    double length = 0;

    Vlist *vlistObj = new Vlist();
    Video *videoObj = nullptr;

    while (getline(std::cin, user)) {
        if (user == "insert") {
            if (getline (std::cin, title) &&
                getline (std::cin, url) &&
                getline (std::cin, comment) &&
                std::cin >> length &&
                std::cin >> rating) {
                    std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
                    videoObj = new Video (title, url, comment, length, rating);
                    vlistObj->Insert(videoObj);
            }
        }
        else if (user == "lookup") {
            if (getline(std::cin, title)) {
                videoObj = new Video (title);   /* you must construct a new videoObj */
                Video *video = nullptr;         /* you want a Video* pointer returned */
                if ((video = vlistObj->lookup(videoObj))) { /* lookup & assign return */
                    video->print();             /* output the video, not list */
                }
                else {
                    std::cout << "title not found: '" << title << "'.\n";
                }
            }
        }
        else if (user == "print") {
            std::cout << "\nlist content:\n";
            vlistObj->print();
        }
    }
}

输入文件示例

为了尽量减少代码的运行,您需要在链表中添加多个节点。尝试查找不存在的节点怎么样——验证该代码路径?

$ cat dat/ll_video2.txt
insert
Arthur Benjamin: Lightning calculation and other "Mathemagic"
http://www.youtube.com/watch?v=M4vqr3_ROIk
Hard to believe.
15.25
4
insert
Arthur Benjamin: Some Other "Mathemagic"
http://www.youtube.com/watch?v=SomeOther
Hard to swallow.
25.25
7
lookup
Arthur Benjamin: Lightning calculation and other "Mathemagic"
lookup
Arthur Benjamin: Some Other "Mathemagic"
lookup
Mickey & Minnie do Disney
print

使用/输出示例

三个查找的结果得到正确处理,列表内容按原样打印到print 命令输入,作为上面输入的最后一行:

$ ./bin/ll_video <dat/ll_video2.txt
Arthur Benjamin: Lightning calculation and other "Mathemagic", http://www.youtube.com/watch?v=M4vqr3_ROIk, Hard to believe., 15.25, 4
Arthur Benjamin: Some Other "Mathemagic", http://www.youtube.com/watch?v=SomeOther, Hard to swallow., 25.25, 7
title not found: 'Mickey & Minnie do Disney'.

list content:
Arthur Benjamin: Lightning calculation and other "Mathemagic", http://www.youtube.com/watch?v=M4vqr3_ROIk, Hard to believe., 15.25, 4
Arthur Benjamin: Some Other "Mathemagic", http://www.youtube.com/watch?v=SomeOther, Hard to swallow., 25.25, 7

查看一下,如果您还有其他问题,请告诉我。

【讨论】:

  • 哦,哇。没想到我有这么多错误。是的,你绝对是对的。在开始输入任何内容之前,我确实需要查看我想要完成的任务。非常感谢您花时间帮助我纠正我的错误。信息量很大。谢谢
  • 不用担心,很高兴为您提供帮助。我们都从同一个地方开始。看看你对other 的需求——如果你可以将std::string 传递给lookup(),这会让事情变得容易得多。很好地锁定您的编码。
【解决方案2】:

错误在Vlist::lookup函数中,当前节点指针指向m_next,然后指向m_videom_next不是必需的,m_head应该直接指向m_video

在完整的工作代码下方,我还在这里和那里进行了一些更改,以消除编译器中的所有警告

#include <iostream>
#include <stdlib.h>
#include <cstring>
using namespace std;

class Video {

  public:
    Video(string video_title, string video_link, string video_comment, double video_length, int video_number);
    void print();
    const string& GetTitle() const { return title; }

  private:

    string title;
    string link;
    string comment;
    double length;
    int rating;

};


Video::Video(string video_title, string video_link, string video_comment, double video_length, int video_number)
  : title(video_title), link(video_link), comment(video_comment), length(video_length), rating(video_number)
{
}

void Video::print(){

  cout << title << ", " << link << ", " << comment << ", " << length << ", " << rating << endl;

}


class Vlist {
 public:
  Vlist():m_head(nullptr) {}     // init_list
  bool lookup(const string& title);  // gets user input directly
  void Insert(Video *video);
  void print();
  Video* get(const string& title);   // new:returns pointer in list with given title
 private:
 class Node {
        public:
                Node(Video *video, Node *next):m_video(video), m_next(next) {}   // init_list
                Video *m_video;
                Node *m_next;
            } *m_head;    // declared directly together with class definition
 };


void Vlist::Insert(Video* video)
{
    if (m_head == NULL || m_head->m_video -> GetTitle() > video->GetTitle())
    {
        m_head = new Node(video, m_head);
    }
    else
    {
        Node *node = m_head;
        while (node->m_next != NULL && node->m_next -> m_video->GetTitle() < video->GetTitle())
        {
            node = node->m_next;
        }
        node->m_next = new Node(video, node->m_next);
    }
 }

bool Vlist::lookup(const string& title)
{
  Node *node = m_head;
  while (node->m_next != NULL && node-> m_video->GetTitle() != title)
  {
    node = node->m_next;
  }

  return node-> m_video->GetTitle() == title; // there was one pointer too many here
}

 void Vlist::print()
 {
     Node *node = m_head;

     while(node != NULL)
     {
        node -> m_video-> Video::print();
        node = node->m_next;
     }
 }

Video* Vlist::get(const string& title) // returns required item from list
{
    Node *node = m_head;
    while (node != NULL) {
        if (node->m_video->GetTitle() == title)
            return node->m_video;
        node = node->m_next;
    }

return nullptr;
}


int main()
{
  string sort_type, url, comment, title, user;
    int rating;
    double length;

    Vlist *vlistObj= new Vlist;
    Video *videoObj;

    while (getline(cin,user)) {

        if(user == "insert")
        {
        getline(cin,title);
        getline(cin, url);
        getline(cin, comment);
        cin >> length;
        cin >> rating;
        cin.ignore();

        videoObj = new Video(title, url, comment, length, rating);
        vlistObj->Insert(videoObj);
        }

        if(user == "lookup")   // more than a few changes here
        {
            getline(cin, title);

            if (vlistObj -> lookup(title))
            {
                videoObj = vlistObj->get(title);
                videoObj->print();
            } else {
                cout << "not found!\n";
            }
        }
        if(user == "print")
        {
            vlistObj->print();
        }
    }

}

大编辑

以前的版本没有正确遍历Vlist

现在Vlist 已被lookup 命令正确搜索,从而最终打印出正确的Video

【讨论】:

  • 您仍在将未初始化的指针传递给 lookup 函数,然后尝试取消引用该函数。这是未定义的行为。
  • 嗨@JohnFilleau,当用户插入带有Vlist::insert 的电影时,videoObj 指针被分配。 mainwhile 循环中第一个 if 内的倒数第二行。
  • @Lingo 谢谢你这绝对有效。但是,它仍在打印所有内容。有什么想法只打印我想查找的内容吗?
  • @SeanPixel :我加快了之前版本的发布,实际上是因为列表没有正确遍历而出现了错误。现在一切正常,lookup 打印自己的项目。
  • 你可以完全摆脱Vlist::lookup功能,只使用新引入的Vlist::get,说实话
猜你喜欢
  • 1970-01-01
  • 2019-09-17
  • 1970-01-01
  • 2014-02-12
  • 2021-07-11
  • 2014-07-26
  • 1970-01-01
  • 1970-01-01
  • 2012-09-09
相关资源
最近更新 更多