diff --git a/YASI_12/alg.graph.h b/YASI_12/alg.graph.h new file mode 100644 index 0000000..6368109 --- /dev/null +++ b/YASI_12/alg.graph.h @@ -0,0 +1,116 @@ +#pragma once +#include "common.h" +#include "ds.graph.h" +#include "ds.doublylinkedlist.h" +using namespace std; + +namespace yasi{ +namespace alg{ + +namespace graph{ + + + template // graph + class BFS{ + public: + typedef typename G::VertexType VertexType; + typedef typename G::EdgeType EdgeType; + typedef typename G::VertexIterator VertexIterator; + typedef typename G::EdgeIterator EdgeIterator; + typedef typename G::VertexList VertexList; + protected: + enum { + VERTEX_UNDISCOVERED = 0, + VERTEX_DISCOVERED, + VERTEX_VISITING, + VERTEX_VISITED + }; + const G& g; + // the BFS ordering of vertices + int* pBfsParent; + void clear(){ + + } + public: + BFS(const G& g):g(g){ + pBfsParent = new int[g.numVertices()]; + } + virtual ~BFS(){ + DELETE_ARR_SAFE(pBfsParent); + } + /////////////////////////////////////////////////// + // override thesse function in child classes + // for your own BSF visit action + virtual void visitVertexBeforeSniffingNeighbors(VertexType* v){ + // do nothing + } + virtual void visitVertexAfterSniffingNeighbors(VertexType* v){ + // do nothing + } + virtual void visitEdge(EdgeType* e){ + // do nothing + } + ////////////////////////////////////////////////// + + // perform bfs + // the BFS tree is saved in BFS::pVertexSequence + // the vertex id's must be in the range [0..n-1] + void search(const G& g, const VertexType* src) const{ + // init + int n = g.numVertices(); + const int srcId = src->id; + DoublyLinkedList q; + + int* vertexStatus = new int[n]; + for (int i = 0; i < n; i++){ + if (i == srcId) continue; + vertexStatus[i] = VERTEX_UNDISCOVERED; + // parent of each vertex in BFS tree is initially NULL + pBfsParent[i] = -1; + } + + // the source vertex + vertexStatus[srcId] = VERTEX_DISCOVERED; + pBfsParent[srcId] = -1; + q.pushBack(src); + + // the traversal + while (!q.empty()){ + VertexType* currentVertex = q.popFront(); + int currentId = currentVertex->id; + + // visit currentVertex + vertexStatus[currentId] = VERTEX_VISITING; + visitVertexBeforeSniffingNeighbors(currentVertex); + + for (EdgeIterator i = v->pOutEdges->begin(); i != v->pOutEdges->end(); i++){ + EdgeType* e = *i; + // process this edge + visitEdge(e); + + VertexType* neighborVertex = e->end; + int neighborId = neighborVertex->id; + if (vertexStatus[neighborId] == VERTEX_UNDISCOVERED){ + // schedule a visit to the neighbor + vertexStatus[neighborid] == VERTEX_DISCOVERED; + pBfsParent[neighborVertex] = currentVertex; + q.pushBack(neighborVertex); + } + + } + + // done visiting current vertex + visitVertexAfterSniffingNeighbors(currentVertex); + vertexStatus[currentId] = VERTEX_VISITED; + + } + + // cleanup + DELETE_SAFE_ARR(vertexVisited); + } + + }; + +} // namespace graph +} +} \ No newline at end of file diff --git a/YASI_12/ds.graph.h b/YASI_12/ds.graph.h index db95213..fcfd6a9 100644 --- a/YASI_12/ds.graph.h +++ b/YASI_12/ds.graph.h @@ -9,10 +9,6 @@ using namespace std; namespace yasi{ namespace ds{ -// forward declarations -class VertexBase; -class EdgeBase; - template struct Edge{ Element element; @@ -29,8 +25,11 @@ struct Vertex{ typedef Edge edge; public: typedef DoublyLinkedList< edge* > EdgeList; - int id; - Element element; + + int id; // id assigned by the graph data structure + int label; // label assigned with input description + Element element; // possibly composite information stored at this vertex + EdgeList* pInEdges; EdgeList* pOutEdges; @@ -63,12 +62,18 @@ class Graph : public IGraph{ typedef Edge EdgeType; typedef DoublyLinkedList VertexList; typedef DoublyLinkedList EdgeList; + typedef VertexList::iterator VertexIterator; + typedef EdgeList::iterator EdgeIterator; protected: VertexList vertexList; EdgeList edgeList; const bool _weighted; + // this is the id of the next vertex + int _vertexId; + void clear(){ + _vertexId = 0; for (VertexList::iterator i = vertexList.begin(); i != vertexList.end(); i++){ DELETE_SAFE(*i); } @@ -79,9 +84,10 @@ class Graph : public IGraph{ edgeList.clear(); } public: - Graph(bool weighted = true) :_weighted(weighted){ + Graph(bool weighted = true) :_weighted(weighted), _vertexId(0){ } virtual ~Graph(){ + clear(); } int numVertices() const override{ return vertexList.size(); @@ -96,28 +102,46 @@ class Graph : public IGraph{ v2->pInEdges->pushBack(edge); return edge; } - VertexType* addVertex(int id){ - VertexType* pVertex = new VertexType(id); + VertexType* addVertex(int label){ + VertexType* pVertex = new VertexType(_vertexId++); + pVertex->label = label; vertexList.pushBack(pVertex); return pVertex; } - VertexType* addVertex(int id, VertexElement e){ + VertexType* addVertex(int label, VertexElement e){ VertexType* pVertex = addVertex(id); pVertex->element = e; return pVertex; } + VertexList* neighbors(const VertexType* v) const{ + return + } // strEdgeList contains non-empty lines // one line for each edge // line format: vid1 vid2 weight void loadFromString(string strEdgeList){ clear(); + // cleanup the input string of all '\r' and flanking '\r\n' + strEdgeList = strPurge(strEdgeList, "\r"); stringstream inp(trim(strEdgeList)); + // comments char + string commentChars = ";#"; // use a temporary hashtable IntLinearProbingHashTable< VertexType* > ht; while (! inp.eof()){ + // comment line + char leadingChar = inp.peek(); + if (commentChars.find(leadingChar) != string::npos ||// comment + leadingChar == '\n'){ // empty line + // skip this comment line + inp.ignore(INT_MAX, '\n'); + continue; + } + + int vid1, vid2; float w; // vertices @@ -139,7 +163,7 @@ class Graph : public IGraph{ ppv1 = ht.get(vid1); if (!ppv1){ pv1 = addVertex(vid1); - ht.put(vid1, pv1); + ht.put(vertexId, pv1); } else{ pv1 = *ppv1; @@ -156,24 +180,71 @@ class Graph : public IGraph{ // create edge addEdge(pv1, pv2, w); } + + + } + + string toString(){ + stringstream str; + str << "; Number of vertices" << endl << numVertices() << endl + << "; Number of edges" << endl << numEdges() << endl; + str << "; List of edges" << endl; + for (VertexList::iterator i = vertexList.begin(); i != vertexList.end(); i++){ + // for each vertex + VertexType* v = *i; + for (EdgeList::iterator j = v->pOutEdges->begin(); j != v->pOutEdges->end(); j++){ + // for each outgoing edge + EdgeType* e = *j; + str << e->start->id << " " << e->end->id; + if (_weighted){ + str << " " << e->weight; + } + str << endl; + } + } + return str.str(); } }; // class Graph +template +ostream& operator<<(ostream& out, const Graph& g){ out << g.toString(); return out; } + class GraphTest : public yasi::Test{ public: void loadFromString(){ Graph g; + typedef Graph::VertexIterator VertexIterator; // simple triangle - string triangle = + string triangle = + "; this is comment\n" "1 2 12\n" "1 3 13\n" - "2 3 23\n"; + "\n" + "; some other comments\n" + "2 3 23\n" + "; finishing comments"; + const string strTriangle = + "; Number of vertices\n" + "3\n" + "; Number of edges\n" + "3\n" + "; List of edges\n" + "1 2 12\n" + "1 3 13\n" + "2 3 23\n" + ; g.loadFromString(triangle); // test ASSERT_EQ(3, g.numVertices()); ASSERT_EQ(3, g.numEdges()); + // now make sure that all id's are different, and they are in the range [1..n-1] + for (VertexIterator i = g.vertexList.begin(); i != g.vertexList.end(); i++){ + ASSERT_LE(0, (*i)->id); + ASSERT_GT(g.numVertices(), (*i)->id); + } + ASSERT_EQ(strTriangle, g.toString()); } };