Created
November 2, 2019 13:48
-
-
Save simondegheselle/dd104b555a2ed41b0df968bc980336bb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Created by simondegheselle on 11/1/19. | |
// | |
#ifndef COMPONENTGRAPH_GRAAF_H | |
#define COMPONENTGRAPH_GRAAF_H | |
#include <cassert> | |
#include <fstream> | |
#include <string> | |
#include <sstream> | |
#include <cstring> | |
#include <stdexcept> | |
#include <iostream> | |
#include <sstream> | |
#include <vector> | |
#include <map> | |
#include <stack> | |
#include <exception> | |
enum RichtType { GERICHT, ONGERICHT }; | |
enum Color { | |
WHITE, GRAY, BLACK | |
}; | |
class GraafExceptie : public std::logic_error { | |
public: | |
GraafExceptie(const std::string &boodschap_) : std::logic_error(boodschap_) {} | |
}; | |
std::ostream &operator<<(std::ostream &os, const GraafExceptie& exc){ | |
return os << exc.what(); | |
} | |
template<RichtType RT> | |
class Graaf{ | |
public: | |
typedef std::map<int, int> Burenlijst; // beeldt knoopnummer (van buur) af op verbindingsnummer | |
void reverse_graph(); | |
// Construeert een graaf met gegeven RichtType en aantal knopen (default 0), zonder verbindingen. | |
Graaf(int n=0); | |
// Geeft true indien het richttype GERICHT is, false indien het ONGERICHT is. | |
bool isGericht() const; | |
// Voegt een nieuwe 'lege' knoop toe, d.w.z. zonder verbindingen. | |
// Geeft knoopnummer van toegevoegde knoop terug (begint bij 0). | |
virtual int voegKnoopToe(); | |
// Voegt verbinding toe tussen knoopnummers 'van' en 'naar'. | |
// Gooit GraafExceptie indien verbinding al bestaat of knoopnummers ongeldig zijn. | |
// Geeft verbindingsnummer van toegevoegde verbinding terug (begint bij 0). | |
// Bij ongerichte graaf wordt terugverbinding ook toegevoegd (met zelfde verbindingnummer!) | |
virtual int voegVerbindingToe(int van, int naar); | |
// Verwijdert de verbinding tussen knoopnummers 'van' en 'naar', incl. de terugverbinding indien ongericht. | |
// Gooit GraafExceptie indien knoopnummers ongeldig zijn. | |
// Geen exceptie indien de verbinding niet bestond. | |
// Andere verbindingen worden niet 'hernummerd'. Er komen dus mogelijks "gaten" in de verbindingsnummers. | |
virtual void verwijderVerbinding(int van, int naar); | |
// Geeft het aantal knopen van de graaf. | |
int aantalKnopen() const; | |
// Geeft het aantal verbindingen van de graaf. | |
// Bij ongerichte graaf worden verbindingen NIET dubbel geteld! | |
int aantalVerbindingen() const; | |
// Geeft het verbindingsnummer van de verbinding tussen 'van' en 'naar'. | |
// Geeft -1 indien die verbinding niet bestaat. | |
// Gooit een GraafExceptie indien knoopnummers ongeldig zijn. | |
// Opgelet: performantie is O(log(v)) waarbij v aantal verbindingen vanuit 'van' is. | |
int verbindingsnummer(int van, int naar) const; | |
std::stack<int> depth_first_search(int begin_node, std::vector<Color>& colors, std::stack<int>& post_order); | |
void evaluate(int begin_node, std::vector<Color> &colors, std::stack<int>& post_order); | |
// Verwijdert alle knopen en verbindingen en herstart de verbindingsnummer | |
virtual void wis(); | |
// Toegang tot de burenlijsten: | |
const Burenlijst &operator[](int i) const { return burenlijsten[i]; } | |
Burenlijst &operator[](int i) { return burenlijsten[i]; } // deze kan als lvalue gebruikt worden | |
// Schrijft de gegevens van de volledige graaf naar outputstream os. | |
virtual void schrijf(std::ostream &os) const; | |
virtual void teken(const char* bestandsnaam) const; | |
virtual std::string knooplabel(int i) const{ | |
std::ostringstream uit; | |
uit<<i; | |
return uit.str(); | |
}; | |
virtual std::string taklabel(int i) const{return "";}; | |
// Schrijft de gegevens van de knoop met knoopnummer k naar outputstream os. | |
virtual void schrijfKnoop(std::ostream &os, int k) const; | |
// Schrijft de gegevens van de verbinding met verbindingsnummer v naar outputstream os. | |
virtual void schrijfVerbinding(std::ostream &os, int v) const; | |
protected: | |
// hulpfuncties | |
void controleerKnoopnummer(int k) const; // throw indien k ongeldig | |
void voegVerbindingToeInDatastructuur(int van, int naar,int verbindingsnummer); | |
void verwijderVerbindingUitDatastructuur(int van, int naar); | |
protected: | |
//datavelden | |
std::vector<Burenlijst> burenlijsten; | |
int hoogsteVerbindingsnummer; | |
RichtType richttype; | |
std::stack<int> vrijgekomenVerbindingsnummers; | |
}; | |
template<RichtType RT> | |
std::ostream &operator<<(std::ostream& os, const Graaf<RT>& g); | |
// --- implementatie --- | |
template<RichtType RT> | |
void Graaf<RT>::controleerKnoopnummer(int k) const{ | |
if (k<0 || (size_t)k>=burenlijsten.size()){ | |
std::ostringstream out; | |
out << "Ongeldig knoopnummer "<<k | |
<<". Moet >= 0 en < "<<burenlijsten.size()<<" zijn."; | |
throw GraafExceptie(out.str()); | |
} | |
} | |
template<RichtType RT> | |
Graaf<RT>::Graaf(int n) : burenlijsten(n), hoogsteVerbindingsnummer(0){}; | |
template<RichtType RT> | |
bool Graaf<RT>::isGericht() const { return true; }//voor gerichte graaf | |
template<> | |
bool Graaf<ONGERICHT>::isGericht() const { return false; }//voor ongerichte graaf | |
template<RichtType RT> | |
int Graaf<RT>::voegKnoopToe(){ | |
int n = burenlijsten.size(); | |
burenlijsten.resize(n+1); // default constructor voor nieuwe knoop wordt opgeroepen (hier lege map) | |
return n; | |
} | |
template<RichtType RT> | |
int Graaf<RT>::voegVerbindingToe(int van, int naar){ | |
controleerKnoopnummer(van); | |
controleerKnoopnummer(naar); | |
if (burenlijsten[van].count(naar) > 0) | |
{ | |
std::ostringstream out; | |
out << "verbinding " << van << "-" << naar << " bestaat al"; | |
throw GraafExceptie(out.str()); | |
} | |
else { | |
int verbindingsnummer; | |
if (!vrijgekomenVerbindingsnummers.empty()){ | |
verbindingsnummer=vrijgekomenVerbindingsnummers.top(); | |
vrijgekomenVerbindingsnummers.pop(); | |
}else | |
verbindingsnummer=hoogsteVerbindingsnummer++; | |
voegVerbindingToeInDatastructuur(van,naar,verbindingsnummer); | |
return verbindingsnummer; | |
} | |
} | |
template<RichtType RT>//voor gerichte graaf | |
void Graaf<RT>::voegVerbindingToeInDatastructuur(int van, int naar, int verbindingsnummer){ | |
burenlijsten[van][naar] = verbindingsnummer; | |
} | |
template<> | |
void Graaf<ONGERICHT>::voegVerbindingToeInDatastructuur(int van, int naar, int verbindingsnummer){ | |
burenlijsten[van][naar] = verbindingsnummer; | |
burenlijsten[naar][van] = verbindingsnummer; | |
} | |
template<RichtType RT> | |
void Graaf<RT>::verwijderVerbinding(int van, int naar){ | |
controleerKnoopnummer(van); | |
controleerKnoopnummer(naar); | |
if (burenlijsten[van].find(naar)!=burenlijsten[van].end()){//verbinding bestaat | |
vrijgekomenVerbindingsnummers.push(burenlijsten[van][naar]); | |
verwijderVerbindingUitDatastructuur(van,naar); | |
} | |
// (geen exception indien verbinding niet bestaat) | |
} | |
template<RichtType RT> | |
void Graaf<RT>::verwijderVerbindingUitDatastructuur(int van, int naar){ | |
burenlijsten[van].erase(naar); | |
} | |
template<> | |
void Graaf<ONGERICHT>::verwijderVerbindingUitDatastructuur(int van, int naar){ | |
burenlijsten[van].erase(naar); | |
burenlijsten[naar].erase(van); | |
} | |
template<RichtType RT> | |
int Graaf<RT>::aantalKnopen() const { return burenlijsten.size(); } | |
template<RichtType RT> | |
int Graaf<RT>::aantalVerbindingen() const { | |
return hoogsteVerbindingsnummer-vrijgekomenVerbindingsnummers.size(); | |
} | |
template<RichtType RT> | |
int Graaf<RT>::verbindingsnummer(int van, int naar) const{ | |
controleerKnoopnummer(van); | |
controleerKnoopnummer(naar); | |
Burenlijst::const_iterator verbindingit = burenlijsten[van].find(naar); | |
if (verbindingit == burenlijsten[van].end()) | |
return -1; | |
else | |
return (*verbindingit).second; | |
} | |
template<RichtType RT> | |
void Graaf<RT>::wis(){ | |
burenlijsten.clear(); | |
hoogsteVerbindingsnummer = 0; | |
while (!vrijgekomenVerbindingsnummers.empty()) | |
vrijgekomenVerbindingsnummers.pop(); | |
} | |
template<RichtType RT> | |
void Graaf<RT>::schrijf(std::ostream &os) const{ | |
os << "Graaf: " << aantalKnopen() << " knopen en " | |
<< aantalVerbindingen() << " verbindingen:" << std::endl; | |
for (int k=0; k<aantalKnopen(); k++) | |
schrijfKnoop(os, k); | |
} | |
template<RichtType RT> | |
void Graaf<RT>::teken(const char* bestandsnaam) const{ | |
std::ofstream uit(bestandsnaam); | |
assert(uit); | |
std::string pijl; | |
if (isGericht()){ | |
uit<<"digraph {\n"; | |
pijl="->"; | |
} | |
else{ | |
uit<<"graph {\n"; | |
pijl="--"; | |
}; | |
for (int k=0; k<aantalKnopen(); k++){ | |
if (burenlijsten[k].empty()) | |
uit<<knooplabel(k)<<";\n"; | |
else{ | |
for (auto& [buur,tak]: this->burenlijsten[k]){ | |
//C++-14-versie: | |
// for (auto& p: this->burenlijsten[k]){ | |
// auto& buur=p.first; | |
// auto& tak=p.second; | |
if (isGericht() || buur >= k) | |
uit<<knooplabel(k)<<" "<<pijl<<" " | |
<<knooplabel(buur)<<taklabel(tak)<<";\n"; | |
}; | |
}; | |
}; | |
uit<<"}"; | |
uit.close(); | |
}; | |
template<RichtType RT> | |
void Graaf<RT>::schrijfKnoop(std::ostream &os, int k) const{ | |
os<<"Burenlijst "<<k<<"::\n"; | |
for (auto& [buur,tak]: this->burenlijsten[k]){ | |
//C++-14-versie: | |
// for (auto& p: this->burenlijsten[k]){ | |
// auto& buur=p.first; | |
// auto& tak=p.second; | |
os << " ->" << buur; | |
schrijfVerbinding(os, tak); | |
} | |
} | |
template<RichtType RT> | |
void Graaf<RT>::schrijfVerbinding(std::ostream &os, int v) const{ | |
os << " via verbinding nr. " << v << std::endl; | |
} | |
template<RichtType RT> | |
std::ostream &operator<<(std::ostream &os, const Graaf<RT> &g){ | |
g.schrijf(os); | |
return os; | |
} | |
template<RichtType RT,class Takdata> | |
class GraafMetTakdata: public virtual Graaf<RT>{ | |
public: | |
GraafMetTakdata(int n=0):Graaf<RT>(n){}; | |
//Noot: toevoegfunctie zonder takdata op te geven kan alleen gebruikt als de klasse | |
// Takdata een defaultconstructor heeft. | |
virtual int voegVerbindingToe(int van, int naar); | |
virtual int voegVerbindingToe(int van, int naar, const Takdata&); | |
//Noot: verwijderVerbinding wordt ongewijzigd overgenomen van Graaf! | |
//TakData vrijgeven (eventueel voor wijziging). Geeft nullpointer als tak niet bestaat | |
//Noot: pointers teruggegeven door geefTakdata worden ongeldig | |
//door toevoegen van een tak. | |
const Takdata* geefTakdata(int van,int naar) const; | |
Takdata* geefTakdata(int van,int naar) ; | |
virtual std::string taklabel(int i) const{ | |
std::ostringstream uit; | |
uit<<"[label=\""<<takdatavector[i]<<"\"]"; | |
return uit.str(); | |
}; | |
virtual void wis(); | |
// Schrijft de gegevens van de verbinding met verbindingsnummer v naar outputstream os. | |
virtual void schrijfVerbinding(std::ostream &os, int v) const; | |
protected: | |
std::vector<Takdata> takdatavector; | |
}; | |
template<RichtType RT,class Takdata> | |
int GraafMetTakdata<RT,Takdata>::voegVerbindingToe(int van, int naar){ | |
return this->voegVerbindingToe(van,naar,Takdata()); | |
} | |
template<RichtType RT,class Takdata> | |
int GraafMetTakdata<RT,Takdata>::voegVerbindingToe(int van, int naar, const Takdata& td){ | |
bool isnieuwtaknummer=this->vrijgekomenVerbindingsnummers.empty(); | |
int taknummer=Graaf<RT>::voegVerbindingToe(van,naar); | |
if (isnieuwtaknummer) | |
takdatavector.push_back(td); | |
else | |
takdatavector[taknummer]=td; | |
return taknummer; | |
} | |
template<RichtType RT,class Takdata> | |
const Takdata* GraafMetTakdata<RT,Takdata>::geefTakdata(int van,int naar) const{ | |
int taknummer=this->verbindingsnummer(van,naar); | |
if (taknummer!=-1) | |
return &takdatavector[taknummer]; | |
else | |
return 0; | |
} | |
template<RichtType RT,class Takdata> | |
Takdata* GraafMetTakdata<RT,Takdata>::geefTakdata(int van,int naar){ | |
int taknummer=this->verbindingsnummer(van,naar); | |
if (taknummer!=-1) | |
return &takdatavector[taknummer]; | |
else | |
return 0; | |
} | |
template<RichtType RT,class Takdata> | |
void GraafMetTakdata<RT,Takdata>::wis(){ | |
Graaf<RT>::wis(); | |
takdatavector.clear(); | |
} | |
template<RichtType RT,class Takdata> | |
void GraafMetTakdata<RT,Takdata>::schrijfVerbinding(std::ostream &os, int v) const{ | |
os << " via verbinding nr. " << v <<" (Data: "<<takdatavector[v]<<")"<< std::endl; | |
} | |
template<RichtType RT, class Knoopdata> | |
class GraafMetKnoopdata:public virtual Graaf<RT>{ | |
public: | |
// Construeert een graaf met gegeven RichtType, en lijst van Knoopdata; | |
template<class InputIterator> | |
GraafMetKnoopdata(InputIterator start,InputIterator eind); | |
GraafMetKnoopdata():Graaf<RT>(){}; | |
virtual int voegKnoopToe(); | |
virtual int voegKnoopToe(const Knoopdata&); | |
const Knoopdata* geefKnoopdata(int knoopnr) const; | |
Knoopdata* geefKnoopdata(int knoopnr) ; | |
virtual std::string knooplabel(int i) const{ | |
std::ostringstream uit; | |
uit<<'"'<<i<<":"<<knoopdatavector[i]<<'"'; | |
return uit.str(); | |
}; | |
virtual void wis(); | |
virtual void schrijfKnoop(std::ostream &os, int k) const; | |
protected: | |
//datavelden | |
std::vector<Knoopdata> knoopdatavector; | |
}; | |
template<RichtType RT, class Knoopdata> | |
template<class InputIterator> | |
GraafMetKnoopdata<RT,Knoopdata>::GraafMetKnoopdata(InputIterator start,InputIterator eind) | |
:Graaf<RT>(0){ | |
for(;start!=eind;start++) | |
voegKnoopToe(*start); | |
} | |
template<RichtType RT, class Knoopdata> | |
int GraafMetKnoopdata<RT,Knoopdata>::voegKnoopToe(){ | |
return this->voegKnoopToe(Knoopdata()); | |
} | |
template<RichtType RT, class Knoopdata> | |
int GraafMetKnoopdata<RT,Knoopdata>::voegKnoopToe(const Knoopdata& kd){ | |
int ret=Graaf<RT>::voegKnoopToe(); | |
knoopdatavector.push_back(kd); | |
return ret; | |
} | |
template<RichtType RT,class Knoopdata> | |
const Knoopdata* GraafMetKnoopdata<RT,Knoopdata>::geefKnoopdata(int knoopnummer) const{ | |
this->controleerKnoopnummer(knoopnummer); | |
return &knoopdatavector[knoopnummer]; | |
} | |
template<RichtType RT,class Knoopdata> | |
Knoopdata* GraafMetKnoopdata<RT,Knoopdata>::geefKnoopdata(int knoopnummer){ | |
this->controleerKnoopnummer(knoopnummer); | |
return &knoopdatavector[knoopnummer]; | |
} | |
template<RichtType RT,class Knoopdata> | |
void GraafMetKnoopdata<RT,Knoopdata>::wis(){ | |
Graaf<RT>::wis(); | |
knoopdatavector.clear(); | |
} | |
template<RichtType RT, class Knoopdata> | |
void GraafMetKnoopdata<RT,Knoopdata>::schrijfKnoop(std::ostream &os, int k) const{ | |
os << "knoop " << k <<"(Data: "<<knoopdatavector[k]<< "):" << std::endl; | |
for (auto& [buur,tak]: this->burenlijsten[k]){ | |
//C++-14-versie: | |
// for (auto& p: this->burenlijsten[k]){ | |
// auto& buur=p.first; | |
// auto& tak=p.second; | |
os << " ->" << buur; | |
this->schrijfVerbinding(os, tak); | |
} | |
} | |
template<RichtType RT, class Knoopdata, class Takdata> | |
class GraafMetKnoopEnTakdata:public GraafMetKnoopdata<RT,Knoopdata>, | |
public GraafMetTakdata<RT, Takdata>{ | |
public: | |
template<class InputIterator> | |
GraafMetKnoopEnTakdata(InputIterator start,InputIterator eind): | |
GraafMetKnoopdata<RT,Knoopdata>(start,eind){}; | |
GraafMetKnoopEnTakdata(): | |
GraafMetKnoopdata<RT,Knoopdata>(){}; | |
virtual void wis(){ | |
GraafMetKnoopdata<RT,Knoopdata>::wis(); | |
this->takdatavector.clear(); | |
} | |
}; | |
template <RichtType RT> | |
std::stack<int> Graaf<RT>::depth_first_search(int begin_node, std::vector<Color> &colors, std::stack<int>& post_order) { | |
this->evaluate(begin_node, colors, post_order); | |
return post_order; | |
} | |
template <RichtType RT> | |
void Graaf<RT>::evaluate(int begin_node, std::vector<Color> &colors, std::stack<int> & post_order) { | |
std::map<int, int> buren = this->burenlijsten[begin_node]; | |
colors[begin_node] = GRAY; | |
auto it = buren.begin(); | |
while (it != buren.end()) { | |
if (colors[it->first] == WHITE) { | |
this->evaluate(it->first, colors, post_order); | |
colors[it->first] = BLACK; | |
post_order.push(it->first); | |
} | |
it++; | |
} | |
} | |
template<RichtType RT> | |
void Graaf<RT>::reverse_graph() { | |
Graaf<RT> reversed_graph(this->aantalKnopen()); | |
for(int i = 0 ; i < this->aantalKnopen(); i++) { | |
std::map<int, int> node_n = this->burenlijsten[i]; | |
for(auto & it : node_n) { | |
try { | |
reversed_graph.voegVerbindingToe(it.first, i); | |
} catch(GraafExceptie e) { | |
std::cerr << "Kon omgekeerde verbinding niet toevoegen tussen " << it.first << " en " << i << std::endl; | |
} | |
} | |
} | |
*this = std::move(reversed_graph); | |
} | |
#endif //COMPONENTGRAPH_GRAAF_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment