【问题标题】:Cant push to queue with underlying list无法使用基础列表推送到队列
【发布时间】:2016-07-28 00:27:39
【问题描述】:

按照example from my professor,使用基于列表的队列遍历图:

void breadthFirst (Point* root)
{
   queue<list<Point*> > q;
   q.push (root);
 }

error: no matching function for call to ‘std::queue<std::list<Point*> >::push(Point*&)’
    q.push (root);

同理:

void breadthFirstTraversal (const Point& start)
{
  queue<list<Point> > q;
  q.push (start); 
}      

error: no matching function for call to ‘std::queue<std::list<Point> >::push(const Point&)’
   q.push (start); 

我查看了有关指针和引用的材料,并尝试了不同的传递变量的方法。

根据文档,构造函数似乎不正确std::queue&lt;int,std::list&lt;int&gt;

【问题讨论】:

  • 你真的想要一个点列表队列还是只是一个点队列?
  • @aichao:我也是这么想的,但是他教授的代码就是这样。不过,我不知道他的教授是否曾测试过该代码。
  • 很不幸。我对学术代码的期望不高,但我确实认为提供实用、合理的代码示例符合学生的最大利益。

标签: c++ list graph queue pass-by-reference


【解决方案1】:

Looking at reliable documentation:

template<
    class T,
    class Container = std::deque<T>
> class queue;

我们看到std::queue需要定义为

std::queue<type of element, type of container to base queue on> nameOfQueue;

有两种方法:

queue<Point, list> q;

指定Points 中的queue,其中queue 用来存储Points 的容器是list

 queue<list<Point>>

我们有一个queue,其中包含lists 或Points。我暂时忽略指针,因为它们只是分散注意力。

OP 及其讲师的代码并未明确使用哪种变体。正如 OP 所发现的,不能将Point 推入queue&lt;list&lt;Point&gt;&gt;,因为Point 不是list&lt;Point&gt;

让我们停下来看看提供给 OP 的示例:

void breadthFirst (TreeNode* root)
{
   queue<list<TreeNode*> > q;
   q.push (root); // impossible. root is not list<TreeNode*>
   while (!q.empty())
     {
      v = q.front (); // type of v not specified. Based on usage, assuming TreeNode *
      q.pop ();
      for (int i = 0; i < v->numChildren(); ++i)
        {
         TreeNode* w = v->child[i];
         if (w != 0)
           q.push (w);
        }
     }
}

我可以看到教练的头向后扭曲的地方。我想他们想要

void breadthFirst(std::list<std::list<std::list<...>*>*>* root)

那 ... 代表一个无限的模板递归。祝你好运。教练明智地避免掉进这个兔子洞。

用一个结构来抽象这个混乱

struct TreeNode
{
    std::list <TreeNode* > children;
};

是正确的想法。导师只需要完成它。

现在我们可以定义

std::queue<TreeNode*> q;

q.push(root);

基于对树遍历和现代 C++ 的预先存在的理解(如果没有预先存在的知识给出这样的示例,这将很糟糕),我们会得到类似的东西

#include <list>
#include <queue>
#include <iostream>

void breadthFirst(TreeNode* root)
{
    std::queue<TreeNode*> q;
    q.push(root);
    while (!q.empty())
    {
        TreeNode*v = q.front();
        q.pop();

        std::cout << v->name; // added to visually demonstrate. Do stuff here

        for (TreeNode * w: v->children) // visit all children in order
        {
            // no need to check for nulls. list doesn't need to contain empty slots
            // this was likely a left over from a previous C implementation
            q.push(w);
        }
    }
    std::cout << std::endl;
}

快速测试者:

int main()
{
    TreeNode A('A');
    TreeNode B('B');
    TreeNode C('C');
    TreeNode D('D');
    TreeNode E('E');

    A.children.push_back(&B);
    A.children.push_back(&D);
    B.children.push_back(&E);
    E.children.push_back(&C);

    breadthFirst(&A);
}

五个节点 A 包含 B 和 D。B 包含 C。C 包含 E。

 A
B D
C
E

在广度上,先遍历root,然后依次访问root的所有孩子,再依次访问孩子的孩子,所以

ABDCE

旁注:根据您的运行方式,鉴于现代处理器的预测和缓存能力,std::list 可能不是此处使用的最佳容器。例如,使用 std::vector,处理器总是知道下一个要访问的元素以及在哪里查找它,因此 CPU 可以在读取第一个元素时预加载下一个或下几个元素。使用列表时,项目通常会被打乱,使 CPU 在内存中反弹,在这里读一点,那里读一点,永远没有机会真正超越自己。

【讨论】:

  • 我能够使用queue&lt;Point,list&lt;Point&gt; &gt; q; 进行编译。本课程侧重于标准数据类型和算法分析;我相信在这个队列中使用列表而不是双端队列有 O 优势。
  • 两件事。 1.你可以这样编译,但算法不起作用。 2.忽略实际计算,坚持理论,list 给你 O(1) 的插入和删除,但完全谋杀,O(N),你在遍历。从好的方面来说,对于这种情况,您总是遍历 O(N)。 list 赢了!但是,当您回到实际编程并分析正在运行的代码时,您会发现很多时间list 会因为浪费在等待 RAM 访问上的所有时间而浪费掉。如果 1 需要 145 亿年,就好像 O(1) 有点没用。
猜你喜欢
  • 1970-01-01
  • 2015-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-03
  • 2013-05-26
相关资源
最近更新 更多