Skip to main content
Code Review

Return to Question

Bumped by Community user
replaced http://codereview.stackexchange.com/ with https://codereview.stackexchange.com/
Source Link
Tweeted twitter.com/StackCodeReview/status/684648702481403904
added 665 characters in body
Source Link
Loki Astari
  • 97.7k
  • 5
  • 126
  • 341

As an rags-to-riches version of this Object oriented approach to Dijkstra's algorithm

 */
 std::vector<NodeRef> route(NodeRef const& src, NodeRef const& dst)
 {
 using PriorityQueue = std::priority_queue<QueInfo>;
 set<NodeId> std::set<NodeId> found;
 PriorityQueue  std::priority_queue<QueInfo> frontier;
 frontier.emplace(src, 0, std::vector<NodeRef>());
 while(!frontier.empty()) {
 QueInfo next = frontier.top();
 frontier.pop();
 NodeRef const& current = std::get<0>(next);
 if (found.find(current->id()) != found.end()) {
 continue;
 }
 found.emplace(current->id());
 std::vector<NodeRef> const& result = std::get<2>(next);
 if (current == dst) {
 return result;
 }
 for(auto const& loop: current->getEdges()) {
 frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
 }
 }
 return {};
 }
 };
}
/*
*/
template<typename T>
struct RefPrinter
{
 T const& data;
 RefPrinter(T const& data) : data(data) {}
 friend std::ostream& operator<<(std::ostream& str, RefPrinter const& value)
 {
 return str << value.data->id();
 }
};
int main()
{
 using Graph = ThorsAnvil::Graph<std::string>;
 using Dijkstra = ThorsAnvil::Dijkstra<Graph>;
 Graph graph;
 for(auto const& it : {"a","b","c","d","e","f","g"}) {
 graph.addNode(it);
 }
 for(auto const& it : std::initializer_list<std::pair<std::string, std::string>>{
 {"a","b"},{"b","c"},{"c","d"},
 {"b","a"},{"c","b"},{"d","c"},
 {"c","e"},{"e","f"},{"b","f"},
 {"e","c"},{"f","e"},{"f","b"},
 {"f","g"},{"a","g"},
 {"g","f"},{"g","a"}
 }) {
 graph.addEdge(graph.getRef(it.first), graph.getRef(it.second), 1);
 }
 ThorsAnvil::Dijkstra<Graph>Dijkstra dijkstra(graph);
 auto result = dijkstra.route(graph.getRef("a"), graph.getRef("e"));
 std::copy(std::begin(result), std::end(result),
 std::ostream_iterator<RefPrinter<Graph::NodeRef>>(std::cout, "\n"));
}
 */
 std::vector<NodeRef> route(NodeRef const& src, NodeRef const& dst)
 {
 using PriorityQueue = std::priority_queue<QueInfo>;
  std::set<NodeId> found;
 PriorityQueue  frontier;
 frontier.emplace(src, 0, std::vector<NodeRef>());
 while(!frontier.empty()) {
 QueInfo next = frontier.top();
 frontier.pop();
 NodeRef const& current = std::get<0>(next);
 if (found.find(current->id()) != found.end()) {
 continue;
 }
 found.emplace(current->id());
 std::vector<NodeRef> const& result = std::get<2>(next);
 if (current == dst) {
 return result;
 }
 for(auto const& loop: current->getEdges()) {
 frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
 }
 }
 return {};
 }
 };
}
/*
*/
template<typename T>
struct RefPrinter
{
 T const& data;
 RefPrinter(T const& data) : data(data) {}
 friend std::ostream& operator<<(std::ostream& str, RefPrinter const& value)
 {
 return str << value.data->id();
 }
};
int main()
{
 using Graph = ThorsAnvil::Graph<std::string>;
 Graph graph;
 for(auto const& it : {"a","b","c","d","e","f","g"}) {
 graph.addNode(it);
 }
 for(auto const& it : std::initializer_list<std::pair<std::string, std::string>>{
 {"a","b"},{"b","c"},{"c","d"},
 {"b","a"},{"c","b"},{"d","c"},
 {"c","e"},{"e","f"},{"b","f"},
 {"e","c"},{"f","e"},{"f","b"},
 {"f","g"},{"a","g"},
 {"g","f"},{"g","a"}
 }) {
 graph.addEdge(graph.getRef(it.first), graph.getRef(it.second), 1);
 }
 ThorsAnvil::Dijkstra<Graph> dijkstra(graph);
 auto result = dijkstra.route(graph.getRef("a"), graph.getRef("e"));
 std::copy(std::begin(result), std::end(result),
 std::ostream_iterator<RefPrinter<Graph::NodeRef>>(std::cout, "\n"));
}

As an rags-to-riches version of this Object oriented approach to Dijkstra's algorithm

 */
 std::vector<NodeRef> route(NodeRef const& src, NodeRef const& dst)
 {
 std::set<NodeId> found;
 std::priority_queue<QueInfo> frontier;
 frontier.emplace(src, 0, std::vector<NodeRef>());
 while(!frontier.empty()) {
 QueInfo next = frontier.top();
 frontier.pop();
 NodeRef const& current = std::get<0>(next);
 if (found.find(current->id()) != found.end()) {
 continue;
 }
 found.emplace(current->id());
 std::vector<NodeRef> const& result = std::get<2>(next);
 if (current == dst) {
 return result;
 }
 for(auto const& loop: current->getEdges()) {
 frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
 }
 }
 return {};
 }
 };
}
/*
*/
template<typename T>
struct RefPrinter
{
 T const& data;
 RefPrinter(T const& data) : data(data) {}
 friend std::ostream& operator<<(std::ostream& str, RefPrinter const& value)
 {
 return str << value.data->id();
 }
};
int main()
{
 using Graph = ThorsAnvil::Graph<std::string>;
 using Dijkstra = ThorsAnvil::Dijkstra<Graph>;
 Graph graph;
 for(auto const& it : {"a","b","c","d","e","f","g"}) {
 graph.addNode(it);
 }
 for(auto const& it : std::initializer_list<std::pair<std::string, std::string>>{
 {"a","b"},{"b","c"},{"c","d"},
 {"b","a"},{"c","b"},{"d","c"},
 {"c","e"},{"e","f"},{"b","f"},
 {"e","c"},{"f","e"},{"f","b"},
 {"f","g"},{"a","g"},
 {"g","f"},{"g","a"}
 }) {
 graph.addEdge(graph.getRef(it.first), graph.getRef(it.second), 1);
 }
 Dijkstra dijkstra(graph);
 auto result = dijkstra.route(graph.getRef("a"), graph.getRef("e"));
 std::copy(std::begin(result), std::end(result),
 std::ostream_iterator<RefPrinter<Graph::NodeRef>>(std::cout, "\n"));
}
added 665 characters in body
Source Link
Loki Astari
  • 97.7k
  • 5
  • 126
  • 341

##A very simple Graph object: The graph type here is only supposed to provide a very simplistic implementation of a graph. The point is that it provides the minimum requirements that are needed by Dijkstra algorithm.

*/
namespace ThorsAnvil
{
 template<typename N, typename IdType = N>
 class Graph
 {
 class Node;
 using NodeHolder = typename std::set<Node>;
 public:
 using NodeId = IdType;
 using NodeRef = typename NodeHolder::iterator;
 using Edges = std::vector<std::pair<NodeRef, int>>;
 private:
 class Node
 {
 N data;
 mutable Edges outedge;
 public:
 Node(N const& data)
 : data(data)
 {}
 void addEdge(NodeRef e, int cost) const
 {
 outedge.emplace_back(e, cost);
 }
 NodeId const& id() const
 {
 return data;
 }
 Edges const& getEdges() const
 {
 return outedge;
 }
 friend bool operator<(Node const& lhs, Node const& rhs)
 {
 return lhs.data < rhs.data;
 }
 };
 NodeHolder nodes;
 public:
 NodeRef addNode(N const& data)
 {
 auto result = nodes.emplace(data);
 return result.first;
 }
 NodeRef getRef(N const& data)
 {
 return nodes.find(data);
 }
 void addEdge(NodeRef src, NodeRef dst, int cost)
 {
 if (src != nodes.end() && dst != nodes.end()) {
 src->addEdge(dst, cost);
 }
 }
 Edges const& getEdges(N const& node) const
 {
 static Edges const empty;
 NodeRef nodeInfo = nodes.find(node);
 if (nodeInfo == nodes.end()) {
 return empty;
 }
 return nodeInfo->getEdges();
 }
 };
 /*

QueInfo structure:Dijkstra class

 */
 //template<typename ItsGraph>
 a tuple really: class Dijkstra
 //{
 1: //  Graph: The nodegraph type we havewill reached.traverse
 // 2Graph::NodeRef The cost to get Type that defines references to thisthe nodenodes.
 // 3Graph::NodeId An ordered list of nodes to get here with thisA cost.
type that uniquely identifies template<typenamea T>node.
 struct QueInfo: public std::tuple<T, int,// std::vector<T>>
 {
 nodeRef->id() public:
 Gives a unique ID that identifies the node.
 QueInfo(QueInfo const&) = default;
 // QueInfo(T const& data, int cost, std::vector<T> const& result)
 So we don't :need std::tuple<T,to int,processes std::vector<T>>(data,it cost,more result)than once.
 // {
 nodeRef->getEdges() returns a container with {NodeRef, Cost}
 std using NodeRef = typename Graph::get<2>(*this).push_back(data);NodeRef;
 using NodeId = typename }Graph::NodeId;
 };
 /*

A cost for ordering the priority queue of QueInfo objects:

QueInfo

 */
 template<typename T> // Its a tuple really:
 // It is used in a priority queue used by the route algorithm
 // 1: The node we have reached.
 // 2: The cost to get to this node.
 // 3: An ordered list of nodes to get here with this cost.
 struct CostQueInfo: public std::tuple<NodeRef, int, std::vector<NodeRef>>
 {
 bool operator public:
 QueInfo(QueInfo const&) = default;
 QueInfo(QueInfo<T>NodeRef const& data, int cost, std::vector<NodeRef> const& route)
 : std::tuple<NodeRef, int, std::vector<NodeRef>>(data, cost, route)
 {
 // Add the current node to the end of the route
 std::get<2>(*this).push_back(data);
 }
 // Allow QueInfo to be ordered (for the priority queue
 friend bool operator<(QueInfo const& lhs, QueInfo<T>QueInfo const& rhs) const
 {
 return std::get<1>(lhs) > std::get<1>(rhs);
 }
 };
 /*

##The Algorithm:

Dijkstra (Members and constructor)

 */
 /*/ Inputs:
 // src: start node inGraph theconst& graph object.graph;
 //  dstpublic: end node in the graph object.
 // graph:Dijkstra(Graph Theconst& graph we are traversing.
 // G::NodeRef Type that defines references to the nodes.)
 //  G::NodeId  A type that uniquely identifies a node.
 // nodeRef->idgraph(graph) Gives a unique ID that identifies the node.
 //  {}
 So we don't need to processes it more than once.
 //*

Dijkstra algorithm implementation.

 nodeRef->getEdges() returns a container*/
 with {NodeRef, Cost}
 template<typename G>
 std::vector<typename G::NodeRef>vector<NodeRef> dijkstraroute(typename G::NodeRef const& src, typename G::NodeRef const& dst, G const& graph)
 {
  using NodeId {
 = typename G::NodeId;
 using NodeRef  PriorityQueue = typename Gstd::NodeRef;priority_queue<QueInfo>;

 using PriorityQueue = std::priority_queue<QueInfo<NodeRef>, std::vector<QueInfo<NodeRef>>, Cost<NodeRef>>;
set<NodeId> found;
 std::set<NodeId> found;
 PriorityQueue frontier;
 frontier.emplace(src, 0, std::vector<NodeRef>());
 while(!frontier.empty()) {
 QueInfo<NodeRef> QueInfo next = frontier.top();
 frontier.pop();
 NodeRef const& current = std::get<0>(next);
 if (found.find(current->id()) != found.end()) {
 continue;
 }
 found.emplace(current->id());
 std::vector<NodeRef> const& result = std::get<2>(next);
 if (current == dst) {
 return result;
 }
 for(auto const& loop: current->getEdges()) {
 frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
 }
 }
 return {};
 }
 };
}
/*
*/
template<typename T>
struct RefPrinter
{
 T const& data;
 RefPrinter(T const& data) : data(data) {}
 friend std::ostream& operator<<(std::ostream& str, RefPrinter const& value)
 {
 return str << value.data->id();
 }
};
int main()
{
 using Graph = ThorsAnvil::Graph<std::string>;
 Graph graph;
 for(auto const& it : {"a","b","c","d","e","f","g"}) {
 graph.addNode(it);
 }
 for(auto const& it : std::initializer_list<std::pair<std::string, std::string>>{
 {"a","b"},{"b","c"},{"c","d"},
 {"b","a"},{"c","b"},{"d","c"},
 {"c","e"},{"e","f"},{"b","f"},
 {"e","c"},{"f","e"},{"f","b"},
 {"f","g"},{"a","g"},
 {"g","f"},{"g","a"}
 }) {
 graph.addEdge(graph.getRef(it.first), graph.getRef(it.second), 1);
 }
 ThorsAnvil::Dijkstra<Graph> dijkstra(graph);

 auto result = dijkstra.route(graph.getRef("a"), graph.getRef("g""e"), graph);
 std::copy(std::begin(result), std::end(result),
 std::ostream_iterator<RefPrinter<Graph::NodeRef>>(std::cout, "\n"));
}

##A very simple Graph object:

*/
namespace ThorsAnvil
{
 template<typename N, typename IdType = N>
 class Graph
 {
 class Node;
 using NodeHolder = typename std::set<Node>;
 public:
 using NodeId = IdType;
 using NodeRef = typename NodeHolder::iterator;
 using Edges = std::vector<std::pair<NodeRef, int>>;
 private:
 class Node
 {
 N data;
 mutable Edges outedge;
 public:
 Node(N const& data)
 : data(data)
 {}
 void addEdge(NodeRef e, int cost) const
 {
 outedge.emplace_back(e, cost);
 }
 NodeId const& id() const
 {
 return data;
 }
 Edges const& getEdges() const
 {
 return outedge;
 }
 friend bool operator<(Node const& lhs, Node const& rhs)
 {
 return lhs.data < rhs.data;
 }
 };
 NodeHolder nodes;
 public:
 NodeRef addNode(N const& data)
 {
 auto result = nodes.emplace(data);
 return result.first;
 }
 NodeRef getRef(N const& data)
 {
 return nodes.find(data);
 }
 void addEdge(NodeRef src, NodeRef dst, int cost)
 {
 if (src != nodes.end() && dst != nodes.end()) {
 src->addEdge(dst, cost);
 }
 }
 Edges const& getEdges(N const& node) const
 {
 static Edges const empty;
 NodeRef nodeInfo = nodes.find(node);
 if (nodeInfo == nodes.end()) {
 return empty;
 }
 return nodeInfo->getEdges();
 }
 };
 /*

QueInfo structure:

 */
 // Its a tuple really:
 // 1: The node we have reached.
 // 2: The cost to get to this node.
 // 3: An ordered list of nodes to get here with this cost.
 template<typename T>
 struct QueInfo: public std::tuple<T, int, std::vector<T>>
 {
 public:
 QueInfo(QueInfo const&) = default;
 QueInfo(T const& data, int cost, std::vector<T> const& result)
 : std::tuple<T, int, std::vector<T>>(data, cost, result)
 {
 std::get<2>(*this).push_back(data);
 }
 };
 /*

A cost for ordering the priority queue of QueInfo objects:

 */
 template<typename T>
 struct Cost
 {
 bool operator()(QueInfo<T> const& lhs, QueInfo<T> const& rhs) const
 {
 return std::get<1>(lhs) > std::get<1>(rhs);
 }
 };
 /*

##The Algorithm:

 */
 // Inputs:
 // src: start node in the graph object.
 //  dst: end node in the graph object.
 // graph: The graph we are traversing.
 // G::NodeRef Type that defines references to the nodes.
 //  G::NodeId  A type that uniquely identifies a node.
 // nodeRef->id() Gives a unique ID that identifies the node.
 //  So we don't need to processes it more than once.
 // nodeRef->getEdges() returns a container with {NodeRef, Cost}
 template<typename G>
 std::vector<typename G::NodeRef> dijkstra(typename G::NodeRef const& src, typename G::NodeRef const& dst, G const& graph)
 {
  using NodeId = typename G::NodeId;
 using NodeRef  = typename G::NodeRef;
 using PriorityQueue = std::priority_queue<QueInfo<NodeRef>, std::vector<QueInfo<NodeRef>>, Cost<NodeRef>>;
 std::set<NodeId> found;
 PriorityQueue frontier;
 frontier.emplace(src, 0, std::vector<NodeRef>());
 while(!frontier.empty()) {
 QueInfo<NodeRef> next = frontier.top();
 frontier.pop();
 NodeRef const& current = std::get<0>(next);
 if (found.find(current->id()) != found.end()) {
 continue;
 }
 found.emplace(current->id());
 std::vector<NodeRef> const& result = std::get<2>(next);
 if (current == dst) {
 return result;
 }
 for(auto const& loop: current->getEdges()) {
 frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
 }
 }
 return {};
 }
}
/*
*/
template<typename T>
struct RefPrinter
{
 T const& data;
 RefPrinter(T const& data) : data(data) {}
 friend std::ostream& operator<<(std::ostream& str, RefPrinter const& value)
 {
 return str << value.data->id();
 }
};
int main()
{
 using Graph = ThorsAnvil::Graph<std::string>;
 Graph graph;
 for(auto const& it : {"a","b","c","d","e","f","g"}) {
 graph.addNode(it);
 }
 for(auto const& it : std::initializer_list<std::pair<std::string, std::string>>{
 {"a","b"},{"b","c"},{"c","d"},
 {"b","a"},{"c","b"},{"d","c"},
 {"c","e"},{"e","f"},{"b","f"},
 {"e","c"},{"f","e"},{"f","b"},
 {"f","g"},{"a","g"},
 {"g","f"},{"g","a"}
 }) {
 graph.addEdge(graph.getRef(it.first), graph.getRef(it.second), 1);
 }
 auto result = dijkstra(graph.getRef("a"), graph.getRef("g"), graph);
 std::copy(std::begin(result), std::end(result),
 std::ostream_iterator<RefPrinter<Graph::NodeRef>>(std::cout, "\n"));
}

##A very simple Graph object: The graph type here is only supposed to provide a very simplistic implementation of a graph. The point is that it provides the minimum requirements that are needed by Dijkstra algorithm.

*/
namespace ThorsAnvil
{
 template<typename N, typename IdType = N>
 class Graph
 {
 class Node;
 using NodeHolder = typename std::set<Node>;
 public:
 using NodeId = IdType;
 using NodeRef = typename NodeHolder::iterator;
 using Edges = std::vector<std::pair<NodeRef, int>>;
 private:
 class Node
 {
 N data;
 mutable Edges outedge;
 public:
 Node(N const& data)
 : data(data)
 {}
 void addEdge(NodeRef e, int cost) const
 {
 outedge.emplace_back(e, cost);
 }
 NodeId const& id() const
 {
 return data;
 }
 Edges const& getEdges() const
 {
 return outedge;
 }
 friend bool operator<(Node const& lhs, Node const& rhs)
 {
 return lhs.data < rhs.data;
 }
 };
 NodeHolder nodes;
 public:
 NodeRef addNode(N const& data)
 {
 auto result = nodes.emplace(data);
 return result.first;
 }
 NodeRef getRef(N const& data)
 {
 return nodes.find(data);
 }
 void addEdge(NodeRef src, NodeRef dst, int cost)
 {
 if (src != nodes.end() && dst != nodes.end()) {
 src->addEdge(dst, cost);
 }
 }
 Edges const& getEdges(N const& node) const
 {
 static Edges const empty;
 NodeRef nodeInfo = nodes.find(node);
 if (nodeInfo == nodes.end()) {
 return empty;
 }
 return nodeInfo->getEdges();
 }
 };
 /*

Dijkstra class

 */
 template<typename Graph>
  class Dijkstra
 {
 //  Graph: The graph type we will traverse
 // Graph::NodeRef  Type that defines references to the nodes.
 // Graph::NodeId A type that uniquely identifies a node.
 // nodeRef->id() Gives a unique ID that identifies the node.
 // So we don't need to processes it more than once.
 // nodeRef->getEdges() returns a container with {NodeRef, Cost}
  using NodeRef = typename Graph::NodeRef;
 using NodeId = typename Graph::NodeId;
 /*

QueInfo

 */
  // Its a tuple really:
 // It is used in a priority queue used by the route algorithm
 // 1: The node we have reached.
 // 2: The cost to get to this node.
 // 3: An ordered list of nodes to get here with this cost.
 struct QueInfo: public std::tuple<NodeRef, int, std::vector<NodeRef>>
 {
  public:
 QueInfo(QueInfo const&) = default;
 QueInfo(NodeRef const& data, int cost, std::vector<NodeRef> const& route)
 : std::tuple<NodeRef, int, std::vector<NodeRef>>(data, cost, route)
 {
 // Add the current node to the end of the route
 std::get<2>(*this).push_back(data);
 }
 // Allow QueInfo to be ordered (for the priority queue
 friend bool operator<(QueInfo const& lhs, QueInfo const& rhs)
 {
 return std::get<1>(lhs) > std::get<1>(rhs);
 }
 };
 /*

Dijkstra (Members and constructor)

 */
 Graph const& graph;
 public:
 Dijkstra(Graph const& graph)
 : graph(graph)
 {}
 /*

Dijkstra algorithm implementation.

 */
 std::vector<NodeRef> route(NodeRef const& src, NodeRef const& dst)
 {
 using PriorityQueue = std::priority_queue<QueInfo>;

 std::set<NodeId> found;
 PriorityQueue frontier;
 frontier.emplace(src, 0, std::vector<NodeRef>());
 while(!frontier.empty()) {
  QueInfo next = frontier.top();
 frontier.pop();
 NodeRef const& current = std::get<0>(next);
 if (found.find(current->id()) != found.end()) {
 continue;
 }
 found.emplace(current->id());
 std::vector<NodeRef> const& result = std::get<2>(next);
 if (current == dst) {
 return result;
 }
 for(auto const& loop: current->getEdges()) {
 frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
 }
 }
 return {};
 }
 };
}
/*
*/
template<typename T>
struct RefPrinter
{
 T const& data;
 RefPrinter(T const& data) : data(data) {}
 friend std::ostream& operator<<(std::ostream& str, RefPrinter const& value)
 {
 return str << value.data->id();
 }
};
int main()
{
 using Graph = ThorsAnvil::Graph<std::string>;
 Graph graph;
 for(auto const& it : {"a","b","c","d","e","f","g"}) {
 graph.addNode(it);
 }
 for(auto const& it : std::initializer_list<std::pair<std::string, std::string>>{
 {"a","b"},{"b","c"},{"c","d"},
 {"b","a"},{"c","b"},{"d","c"},
 {"c","e"},{"e","f"},{"b","f"},
 {"e","c"},{"f","e"},{"f","b"},
 {"f","g"},{"a","g"},
 {"g","f"},{"g","a"}
 }) {
 graph.addEdge(graph.getRef(it.first), graph.getRef(it.second), 1);
 }
 ThorsAnvil::Dijkstra<Graph> dijkstra(graph);

 auto result = dijkstra.route(graph.getRef("a"), graph.getRef("e"));
 std::copy(std::begin(result), std::end(result),
 std::ostream_iterator<RefPrinter<Graph::NodeRef>>(std::cout, "\n"));
}
added 524 characters in body
Source Link
Loki Astari
  • 97.7k
  • 5
  • 126
  • 341
Loading
Correct a typo in "Dijkstra"; Added H3 formatting to the subheadings.
Source Link
glampert
  • 17.3k
  • 4
  • 31
  • 89
Loading
edited tags
Link
200_success
  • 145.6k
  • 22
  • 190
  • 479
Loading
deleted 37 characters in body
Source Link
Loki Astari
  • 97.7k
  • 5
  • 126
  • 341
Loading
Source Link
Loki Astari
  • 97.7k
  • 5
  • 126
  • 341
Loading
lang-cpp

AltStyle によって変換されたページ (->オリジナル) /