【问题标题】:Level order traversal of a tree using std::map::iterators使用 std::map::iterators 对树进行级别顺序遍历
【发布时间】:2019-10-20 06:39:49
【问题描述】:

我有一个由节点组成的树结构。每个节点都有一个 id 和一个 std::map 引用它的所有子节点。

struct Node
{
    Node(std::string id) : id(id) {}
    std::string id;

    Node& operator [] (std::string id)
    {
        iterator it = map.find(id);
        if(it != map.end()) return it->second;
        else
        {
            Node* null = new Node("null");
            return *null;
        }
    }

    void addChild(std::string id)
    {
        Node* child = new Node(id);
        map.insert(std::pair<std::string,Node&>(id,*child));
    }

private:
    std::map<std::string,Node&> map;
};

示例树如下所示

    Node root("root");
    root.addChild("Documents");
    root.addChild("Pictures");
    root.addChild("Music");
    root["Documents"].addChild("Work");
    root["Documents"].addChild("Private");
    root["Documents"].addChild("Home");
    root["Documents"]["Work"].addChild("Finance");
    root["Documents"]["Work"].addChild("Engineering");
    root["Documents"]["Work"]["Finance"].addChild("Week1.xml");
    root["Documents"]["Work"]["Finance"].addChild("Week2.xml");
    root["Documents"]["Work"]["Finance"].addChild("Week3.xml");
    root["Documents"]["Work"]["Finance"].addChild("Week4.xml");
    root["Pictures"].addChild("Tree.jpg");
    root["Pictures"].addChild("Dog.jpg");

我想做一个级别顺序遍历(广度优先遍历)并打印出每个节点的 id。即,打印根目录下的所有节点名称,然后打印根目录下第一个节点下的所有节点名称,然后打印根目录下第二个节点下的节点名称,等等。我希望能够使用一个for循环和迭代器,如下图:

    //ASSIGNEMNT; CONDITION; OPPERATION
    for(Node::iterator it = root.begin(); it != root.end(); root.traverse(it))
    {
        std::cout << it->first << std::endl;
    }

我试图在节点类中定义一个算法来实现这一点。该算法如下图所示:

    typedef std::map<std::string,Node&>::iterator iterator;

    iterator begin()    {return map.begin();}
    iterator end()      {return map.end();}

    iterator traverse(iterator& it)
    {
        std::cout << "Traversing " << this->id << "..." << std::endl;
        it++;
        if(it != it->second.end()) return it;
        else
        {
            iterator next = it->second.begin();
            return next->second.traverse(next);
        }
    }

然而这个函数只遍历root然后退出for循环。实现我想要的正确算法是什么?

完整的Node类和主要功能:

#include <iostream>
#include <map>

using namespace std;

struct Node
{
	Node(std::string id) : id(id) {}
	std::string id;

	typedef std::map<std::string,Node&>::iterator iterator;

	iterator begin()	{return map.begin();}
	iterator end()		{return map.end();}

	iterator traverse(iterator& it)
	{
		std::cout << "Traversing " << this->id << "..." << std::endl;
		it++;
		if(it != it->second.end()) return it;
		else
		{
			iterator next = it->second.begin();
		    return next->second.traverse(next);
		}
	}

	Node& operator [] (std::string id)
	{
		iterator it = map.find(id);
		if(it != map.end()) return it->second;
        else
        {
            Node* null = new Node("null");
            return *null;
        }
	}

	void addChild(std::string id)
	{
		Node* child = new Node(id);
		map.insert(std::pair<std::string,Node&>(id,*child));
	}

private:
	std::map<std::string,Node&> map;
};

int main()
{
	Node root("root");
	root.addChild("Documents");
	root.addChild("Pictures");
	root.addChild("Music");
	root["Documents"].addChild("Work");
	root["Documents"].addChild("Private");
	root["Documents"].addChild("Home");
	root["Documents"]["Work"].addChild("Finance");
	root["Documents"]["Work"].addChild("Engineering");
	root["Documents"]["Work"]["Finance"].addChild("Week1.xml");
	root["Documents"]["Work"]["Finance"].addChild("Week2.xml");
	root["Documents"]["Work"]["Finance"].addChild("Week3.xml");
	root["Documents"]["Work"]["Finance"].addChild("Week4.xml");
	root["Pictures"].addChild("Tree.jpg");
	root["Pictures"].addChild("Dog.jpg");

	for(Node::iterator it = root.begin(); it != root.end(); root.traverse(it))
	{
		std::cout << it->first << std::endl;
	}


	return 0;
}

【问题讨论】:

  • 可能不是您的主要问题,但由于这些行,您正在泄漏内存:Node* child = new Node(id); map.insert(std::pair&lt;std::string,Node&amp;&gt;(id,*child));
  • 您在 Node&amp; operator [] 中有 UB - 如果 if 失败,那么您不会返回任何值。打开编译器警告并修复它们。
  • @πάνταῥεῖ 和@Quimby 这只是我遇到的问题的一个最小工作示例。在我的树结构中,我不使用Node&amp; operator [],除非它会返回一个值。在实际代码中,这是不同的设计。与内存泄漏相同
  • @Blue7 “这只是我遇到的问题的一个最小工作示例” 不,不是。只需阅读minimal reproducible example(再次),了解如何提供。

标签: c++ tree iterator traversal stdmap


【解决方案1】:

您不能使用地图的迭代器,因为每个节点都有不同的地图。您必须定义自己的迭代器类,它可以跟踪当前正在迭代的节点,并在到达该节点的映射末尾时更改为下一个节点。

您的traverse 函数已关闭,但将跳过next 中的第一个迭代器。当它到达最后一个节点(或其映射中没有条目的任何节点)时,它也会遇到未定义行为,因为您将增加 end 迭代器。

一旦遍历移动到其中一个子节点,也会出现未定义行为,因为您随后将比较来自不同容器的迭代器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-28
    • 2021-02-16
    • 1970-01-01
    • 1970-01-01
    • 2022-09-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多