简体   繁体   中英

Level order traversal of a tree using std::map::iterators

I have a tree structure composed of nodes. Each node each has an id and an std::map of references to all of it's child nodes.

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;
};

An example tree is shown below

    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");

I want to do a Level order traversal (breadth first traversal) and print out the id of every node. ie, print all of the names of the nodes under root, then print all of the names of the nodes under the first node under root, then the ones under the second node under root, etc. I want to be able to do this using a for loop and iterators, as shown below:

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

I have attempted to define an algorithm inside the node class that will achieve this. This algorithm is shown below:

    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);
        }
    }

However this function only traverses root and then exits the for loop. What is the correct algorithm to achieve what I want?

Full Node class and main function:

 #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; } 

You can't use map's iterator, since you have a different map in each node. You'll have to define your own iterator class, that can keep track of which node it is currently iterating over and change to the next node when it gets to the end of the map for that node.

Your traverse function is close, but will skip the first iterator in next . It will also run into Undefined Behavior when it reaches the last Node (or any Node with no entries in its map) because you'll increment the end iterator.

There is also Undefined Behavior once the traversal moves to one of the subNodes, since you'll then be comparing iterators from different containers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM