Advertisement
Guest User

Dijkstra's shortest path in C++

a guest
Jun 17th, 2011
462
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.00 KB | None | 0 0
  1. HEADER FILE
  2. ==========================================
  3.  
  4. //////////////////////////////////////////////////////////////////////////
  5. /// Dijkstra's Algorithm is an algorithm written by Professor
  6. /// Edsger Wybe Dijkstra in 1959. It is used to find the shortest path
  7. /// between two nodes in a node graph.
  8. ///
  9. /// This implementation is a modified version of the original algorithm.
  10. ///
  11. /// \author n.foster
  12. //////////////////////////////////////////////////////////////////////////
  13.  
  14. #include <map>
  15. #include <vector>
  16. #include <set>
  17.  
  18. #include "PathNode.h"
  19.  
  20. //////////////////////////////////////////////////////////////////////////
  21. // PathEdge
  22. //
  23. // \brief Represents a two-way connection between two PathNodes.
  24. //        Connection weight is the same in both directions, which is not
  25. //        suitable for something like open-world AI use.
  26. //
  27. // \author n.foster
  28. //////////////////////////////////////////////////////////////////////////
  29. struct PathEdge
  30. {
  31.     PathNode* side1;
  32.     PathNode* side2;
  33.     float weight;
  34.  
  35.     PathEdge( PathNode* side1, PathNode* side2, float weight )
  36.     {
  37.         this->side1 = side1;
  38.         this->side2 = side2;
  39.         this->weight = weight;
  40.  
  41.         side1->AddEdge(this);
  42.         side2->AddEdge(this);
  43.     }
  44.  
  45.     // Returns true if this edge connects to 'targetNode'
  46.     bool ConnectsToNode( PathNode* targetNode )
  47.     {
  48.         return ( side1 == targetNode || side2 == targetNode );
  49.     }
  50.  
  51.     // Pass in a node that is at one side of the edge, it will return the node
  52.     // at the other side, or NULL if the side you passed in is not part of the edge.
  53.     PathNode* GetOtherSide( PathNode* side )
  54.     {
  55.         if ( side1 == side )
  56.         {
  57.             return side2;
  58.         }
  59.         else if ( side2 == side )
  60.         {
  61.             return side1;
  62.         }
  63.         else
  64.         {
  65.             return NULL;
  66.         }
  67.     }
  68. };
  69.  
  70. //////////////////////////////////////////////////////////////////////////
  71. // ShortestPath
  72. //
  73. // \brief Stores a list of PathNodes and allows for calculation of the
  74. //        shortest path, given all connected nodes have a weight for each
  75. //        connection.
  76. //
  77. // \author n.foster
  78. //////////////////////////////////////////////////////////////////////////
  79. class ShortestPath
  80. {
  81. public:
  82.     ShortestPath();
  83.     ~ShortestPath();   
  84.  
  85.     void AddNode( PathNode* newNode );
  86.  
  87.     PathNode* GetNodeByIndex( const unsigned int index ) const;
  88.  
  89.     void Initialize( PathNode* const startNode );
  90.  
  91.     float FindShortestPathToNode( PathNode* targetNode );
  92.    
  93.     void DeletePath( void );
  94.  
  95. private: // Variables
  96.  
  97.     // For each node, the total weight from the source node
  98.     typedef std::pair<unsigned int, float> TIndexDistancePair;
  99.     std::map<unsigned int, float> m_Weights;
  100.  
  101.     // For each node, the previous node in the shortest path
  102.     std::vector<PathNode*> m_NodesInPath;
  103.  
  104.     // List of all nodes in the path
  105.     std::vector<PathNode*> m_Nodes;
  106.  
  107.     // List of nodes that haven't been searched yet
  108.     std::set<PathNode*> m_NodesLeftInSearch;
  109.  
  110.     // Node from which to start a search
  111.     PathNode* m_sourceNode;
  112.  
  113. private: // Members
  114.  
  115.     PathNode* FindNodeClosestToSource( PathNode* sourceNode );
  116.  
  117.     PathNode* FindLowestWeightNodeLeftInSearch(void);
  118. };
  119.  
  120.  
  121. CPP FILE
  122. ==========================================================================
  123. //////////////////////////////////////////////////////////////////////////
  124. /// Dijkstra's Algorithm is an algorithm written by Professor
  125. /// Edsger Wybe Dijkstra in 1959. It is used to find the shortest path
  126. /// between two nodes in a node graph.
  127. ///
  128. /// This implementation is a modified version of the original algorithm.
  129. ///
  130. /// \author n.foster
  131. //////////////////////////////////////////////////////////////////////////
  132.  
  133. #include "ShortestPath.h"
  134. #include <set>
  135. #include <vector>
  136.  
  137. namespace NS_ShortestPath
  138. {
  139.     static const float cfLargeFloat = 10000000.0f;
  140. }
  141.  
  142. ShortestPath::ShortestPath() :
  143. m_sourceNode(NULL)
  144. {
  145. }
  146.  
  147. ShortestPath::~ShortestPath()
  148. {
  149.     for ( int i = m_Nodes.size() - 1; i >= 0; --i )
  150.     {      
  151.         delete m_Nodes[i];
  152.         m_Nodes[i] = NULL;
  153.     }
  154.     m_Nodes.clear();
  155.  
  156.     m_sourceNode = NULL;
  157.  
  158.     m_Weights.clear();
  159.     m_NodesInPath.clear();
  160.     m_NodesLeftInSearch.clear();
  161. }
  162.  
  163. void ShortestPath::Initialize( PathNode* const startNode )
  164. {
  165.     if ( startNode == NULL )
  166.         return;
  167.  
  168.     m_sourceNode = startNode;  
  169.  
  170.     // We know the distance to the source node is always zero
  171.     m_Weights.clear();
  172.     m_Weights.insert( TIndexDistancePair(m_sourceNode->GetIndex(), 0.0f) );
  173.  
  174.     // For each edge connected to the source node, get an index and distance
  175.     std::set<PathEdge*>::iterator it = m_sourceNode->GetEdges().begin();
  176.  
  177.     // For each edge connected to the source node, we setup some initial distances
  178.     for ( it; it != m_sourceNode->GetEdges().end(); ++it )
  179.     {      
  180.         if ( (*it)->side1 == m_sourceNode )
  181.         {          
  182.             m_Weights.insert( TIndexDistancePair( (*it)->side2->GetIndex(), (*it)->weight ));
  183.         }
  184.         else
  185.         {
  186.             m_Weights.insert( TIndexDistancePair( (*it)->side1->GetIndex(), (*it)->weight ));
  187.         }  
  188.     }
  189.  
  190.     // We'll start out our list of nodes in the path by having them all point to the source node
  191.     m_NodesInPath.resize( m_Nodes.size() );
  192.     for ( unsigned int i = 0; i < m_NodesInPath.size(); ++i )
  193.     {
  194.         m_NodesInPath[i] = m_sourceNode;
  195.     }
  196.  
  197.     // Deep copy all nodes into a set of nodes that we have left to search
  198.     m_NodesLeftInSearch.clear();
  199.     for ( unsigned int i = 0; i < m_Nodes.size(); ++i )
  200.     {
  201.         m_NodesLeftInSearch.insert(m_Nodes[i]);
  202.     }
  203.  
  204.     // We don't need to check the source node
  205.     m_NodesLeftInSearch.erase(m_sourceNode);
  206. }
  207.  
  208. void ShortestPath::AddNode( PathNode* newNode )
  209. {
  210.     m_Nodes.push_back(newNode);
  211. }
  212.  
  213. PathNode* ShortestPath::GetNodeByIndex( const unsigned int index ) const
  214. {
  215.     if ( index > (m_Nodes.size() - 1) )
  216.         return NULL;
  217.  
  218.     return m_Nodes[index];
  219. }
  220.  
  221. // Returns weight of shortest path. This is a modified implementation of Dijkstra's algorithm.
  222. float ShortestPath::FindShortestPathToNode( PathNode* targetNode )
  223. {  
  224.     if ( FindNodeClosestToSource( m_sourceNode ) == NULL )
  225.         return NS_ShortestPath::cfLargeFloat;
  226.  
  227.     PathNode* tempNode = NULL;
  228.     float oldDistance = 0.0f;
  229.  
  230.     // See if we've made it to the target node yet
  231.     while ( m_NodesLeftInSearch.find(targetNode) != m_NodesLeftInSearch.end() )
  232.     {
  233.         tempNode = FindLowestWeightNodeLeftInSearch();
  234.         m_NodesLeftInSearch.erase(tempNode);
  235.  
  236.         std::set<PathNode*>::iterator it = m_NodesLeftInSearch.begin();
  237.         for ( it; it != m_NodesLeftInSearch.end(); ++it )
  238.         {
  239.             float weight = NS_ShortestPath::cfLargeFloat;
  240.  
  241.             std::map<unsigned int, float>::iterator distIt = m_Weights.find( (*it)->GetIndex() );          
  242.  
  243.             if ( distIt != m_Weights.end() )
  244.             {          
  245.                 weight = distIt->second;
  246.             }
  247.  
  248.             oldDistance = weight;
  249.  
  250.             if ( tempNode->WeightToNode(*it) == -1.0f )
  251.                 continue;
  252.  
  253.             weight = std::min(oldDistance, m_Weights.find( tempNode->GetIndex() )->second + tempNode->WeightToNode(*it) );         
  254.  
  255.             // If weight has changed, then a shorter path has been found
  256.             if ( weight != oldDistance )
  257.             {
  258.                 if ( distIt == m_Weights.end() )
  259.                 {
  260.                     m_Weights.insert( TIndexDistancePair((*it)->GetIndex(), weight) );
  261.                 }
  262.                 else
  263.                 {
  264.                     distIt->second = weight;
  265.                 }
  266.  
  267.                 m_NodesInPath[ (*it)->GetIndex() ] = tempNode;
  268.             }
  269.         }
  270.     }
  271.  
  272.     // Total up weight of path
  273.     float totalWeight = 0;
  274.  
  275.     // Create a node for iteration
  276.     PathNode* currentNode = targetNode;
  277.  
  278.     // Iterate until the current node is pointing back to the source node
  279.     while ( currentNode != m_sourceNode )
  280.     {  
  281.         PathNode* prevNode = m_NodesInPath[ currentNode->GetIndex() ];
  282.  
  283.         float weightToNode = currentNode->WeightToNode(prevNode);
  284.  
  285.         currentNode->StoreCalculatedWeightToNode(prevNode, weightToNode);
  286.         prevNode->StoreCalculatedWeightToNode(currentNode, weightToNode);
  287.  
  288.         totalWeight += weightToNode;
  289.  
  290.         currentNode = prevNode;
  291.     }
  292.  
  293.     return totalWeight;
  294. }
  295.  
  296. PathNode* ShortestPath::FindNodeClosestToSource( PathNode* sourceNode )
  297. {
  298.     std::set<PathEdge*> edges = sourceNode->GetEdges();
  299.  
  300.     if ( edges.empty() )
  301.         return NULL;
  302.  
  303.     float leastWeight = 10000000;
  304.     PathNode* nodeLeastWeight = NULL;
  305.  
  306.     // Go through all the edges connected to this node and find the one with the least weight
  307.     std::set<PathEdge*>::iterator it = edges.begin();
  308.     for ( it; it != edges.end(); ++it )
  309.     {
  310.         PathEdge* thisEdge = (*it);
  311.  
  312.         if ( thisEdge == NULL )
  313.             continue;
  314.  
  315.         if ( (*it)->weight < leastWeight )
  316.         {
  317.             leastWeight = thisEdge->weight;
  318.             nodeLeastWeight = (thisEdge->side1 == sourceNode) ? thisEdge->side2 : thisEdge->side1;
  319.         }
  320.     }
  321.  
  322.     return nodeLeastWeight;
  323. }
  324.  
  325. // Finds the node with the least weight at a given time during a search.
  326. PathNode* ShortestPath::FindLowestWeightNodeLeftInSearch(void)
  327. {
  328.     float lowestWeight = 10000000.0f;
  329.     PathNode* lowestWeightNode = NULL;
  330.  
  331.     std::set<PathNode*>::iterator nodeIt = m_NodesLeftInSearch.begin();
  332.     for (nodeIt; nodeIt != m_NodesLeftInSearch.end(); ++nodeIt)
  333.     {
  334.         std::map<unsigned int, float>::iterator distIt = m_Weights.find( (*nodeIt)->GetIndex() );
  335.         if ( distIt == m_Weights.end() )
  336.             continue;
  337.  
  338.         if ( distIt->second < lowestWeight )
  339.         {
  340.             lowestWeight = distIt->second;
  341.             lowestWeightNode = GetNodeByIndex(distIt->first);
  342.         }
  343.     }
  344.  
  345.     return lowestWeightNode;
  346. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement