This commit is contained in:
2025-02-21 13:17:35 +01:00
commit e6bb2d584f
135 changed files with 141834 additions and 0 deletions

BIN
P4/ADS_Praktikum_4.pdf Executable file

Binary file not shown.

12
P4/CMakeLists.txt Executable file
View File

@@ -0,0 +1,12 @@
# Set the minimum required version of CMake
cmake_minimum_required(VERSION 3.11)
# Set the project name and specify the C++ as the project language
project(P4_Graph)
# Specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Add an executable with the above sources
add_executable(P4_Graph main.cpp DijkstraSP.cpp EdgeWeightedDigraph.cpp EdgeWeightedGraph.cpp GraphTest.cpp KruskalMST.cpp PrimMST.cpp)

95
P4/DijkstraSP.cpp Executable file
View File

@@ -0,0 +1,95 @@
#include <assert.h>
#include <float.h>
#include <algorithm>
#include "DijkstraSP.h"
/**
* F<>ge eine Kante mit minimalen Kosten hinzu, die von einem
* Baumknoten zu einem Nicht-Baumknoten verl<72>uft und deren
* Ziel w dem Startknoten s am n<>chsten ist.
*
* \param[in] G Kantengewichteter-Digraph
* \param[in] v Zielknoten
*/
void DijkstraSP::relax(EdgeWeightedDigraph G, int v) {
std::vector<DirectedEdge> edges = G[v]; //adjazente Knoten zum Knoten v
for (DirectedEdge e: edges) {
int w = e.to();
if (distToVect[w] > distToVect[v] + e.weight()) {
distToVect[w] = distToVect[v] + e.weight();
edgeTo[w] = e;
if (pq.contains(w))
pq.change(w, distToVect[v]);
else
pq.push(w, distToVect[w]);
}
}
}
/**
* Fuert den Dijkstra Algorithmus von s, im Graph G aus.
*
* \param[in] G Kantengewichteter-Digraph
* \param[in] s Startknoten
*/
DijkstraSP::DijkstraSP(EdgeWeightedDigraph G, int s) {
//Initialisiere distToVect mit "unendlich"
distToVect.resize(G.getV());
for (int v = 0; v < G.getV(); v++) {
distToVect[v] = DBL_MAX;
}
//Setze die Kosten f<>r Startknoten auf 0
distToVect[s] = 0.0;
pq.push(s, 0.0);
//F?ge immer eine Kante mit minimalen Pfadkosten zu s hinzu
while (!pq.empty()) {
int min_node = pq.top().value;
pq.pop();
//F?ge immer eine Kante mit minimalen Pfadkosten zu s der PQ hinzu
relax(G, min_node);
}
}
/**
* Gibt die Distanz von s zum Knoten v zurueck
*
* \param[in] v Zielknoten
* \return Summe der Gewichte auf dem Pfad zu v
*/
double DijkstraSP::distTo(int v) const {
return distToVect[v];
}
/**
* Gibt zurueck ob es einen Pfad zu v von s aus gibt
*
* \param[in] v Zielknoten
* \return true, wenn es einen Pfad von s nach v gibt, sonst false
*/
bool DijkstraSP::hasPathTo(int v) const {
if (distToVect[v] == DBL_MAX)
return false;
return true;
}
/**
* Gibt den Pfad von s nach v als Vektor zurueck
*
* \param[in] v Zielknoten
* \return Vektor mit allen Kanten des Pfades von s nach v
*/
std::vector<DirectedEdge> DijkstraSP::pathTo(int v) {
std::vector<DirectedEdge> path;
DirectedEdge e = edgeTo[v];
while (true) {
path.push_back(e);
if (edgeTo.count(e.from()) > 0) {
e = edgeTo[e.from()];
} else break;
}
std::reverse(path.begin(), path.end());
return path;
}

25
P4/DijkstraSP.h Executable file
View File

@@ -0,0 +1,25 @@
#pragma once
#include <vector>
#include <queue>
#include <map>
#include "EdgeWeightedDigraph.h"
#include "PriorityQueue.h"
class DijkstraSP {
private:
std::map<int, DirectedEdge> edgeTo;
std::vector<double> distToVect;
Utils::PriorityQueue<int> pq;
void relax(EdgeWeightedDigraph G, int v);
public:
DijkstraSP() {
};
DijkstraSP(EdgeWeightedDigraph G, int s);
double distTo(int v) const; // Abst<73>nde vom Startvertex zu v
bool hasPathTo(int v) const; // <20>berpr<70>ft die existens eines Pfades
std::vector<DirectedEdge> pathTo(int v); // Kanten des k<>rzsesten Weges
};

163
P4/EdgeWeightedDigraph.cpp Executable file
View File

@@ -0,0 +1,163 @@
#include <assert.h>
#include <iomanip>
#include <vector>
#include <fstream>
#include <string>
#include "EdgeWeightedDigraph.h"
/**
* Gibt zurueck ob der Knoten v ein gueltiger Knoten im Graph ist
*
* \param[in] v Knoten
* \return Gueltigkeit des Knoten
*/
bool EdgeWeightedDigraph::validateVertex(int v) const {
return v >= 0 && v < this->V;
}
/**
* Gibt eine Fehlermeldung aus, wenn der Knoten v im Graph nicht gueltig ist
*
* \param[in] v Knoten
*/
void EdgeWeightedDigraph::validateVertexWithError(int v) const {
assert(("Vertex is out of bounds!", this->validateVertex(v)));
}
/**
* Erstellt einen Kantengewichteten-Gaph mit V Knoten
*
* \param[in] V Anzahl der Knoten
*/
EdgeWeightedDigraph::EdgeWeightedDigraph(int V)
: V{V}
, E{0} {
adj.resize(V);
}
/**
* Erstellt einen Kantengewichteten-Gaph anhand der Kantenliste in fname
*
* \param[in] fname Dateiname der Kantenliste
*/
EdgeWeightedDigraph::EdgeWeightedDigraph(std::string filename) {
std::ifstream infile(filename);
int tmp_e = 0;
infile >> this->V >> tmp_e;
this->E = 0;
this->adj.resize(this->V, std::vector<DirectedEdge>());
int v, w;
double weight;
while (infile >> v >> w >> weight) {
this->add(DirectedEdge(v, w, weight));
}
assert(("Missing edges!", tmp_e == this->E));
}
/**
* Fuegt die Kante e zum Graphen hinzu
*
* \param[in] e Kante
*/
void EdgeWeightedDigraph::add(DirectedEdge e) {
this->validateVertexWithError(e.from());
this->validateVertexWithError(e.to());
this->adj[e.from()].push_back(e);
this->E++;
}
/**
* Gibt die Anzahl der Knoten zurueck
*
* \return Anzahl der Knoten
*/
int EdgeWeightedDigraph::getV() const {
return this->V;
}
/**
* Gibt die Anzahl der Kanten zurueck
*
* \return Anzahl der Kanten
*/
int EdgeWeightedDigraph::getE() const {
return this->E;
}
/**
* Liefert alle benachbarten Kanten zu v
*
* \param[in] v Knoten von dem aus gesucht wird
* \return Vektor mit allen benachbarten Kanten
*/
std::vector<std::vector<DirectedEdge> > EdgeWeightedDigraph::getAdj() const {
return this->adj;
}
/**
* Liefert alle benachbarten Kanten zu v
*
* \param[in] v Knoten von dem aus gesucht wird
* \return Vektor mit allen benachbarten Kanten
*/
std::vector<DirectedEdge> EdgeWeightedDigraph::getAdj(int v) const {
std::vector<DirectedEdge> neighbors;
for (auto const &n: adj[v]) {
neighbors.push_back(n);
}
return neighbors;
}
/**
* Gibt alle Kanten im Graph zurueck
*
* \return Vektor mit allen Kanten im Graph
*/
std::vector<DirectedEdge> EdgeWeightedDigraph::edges() const {
std::vector<DirectedEdge> list;
for (int v = 0; v < this->V; v++) {
for (DirectedEdge e: this->adj[v]) {
list.push_back(e);
}
}
return list;
}
bool EdgeWeightedDigraph::del_Edge(DirectedEdge e) {
int from = e.from();
int to = e.to();
// ?berpr?fen, ob die Knoten g?ltig sind
if (!validateVertex(from) || !validateVertex(to)) {
return false;
}
// Suchen und Entfernen der Kante aus der Adjazenzliste des Ausgangsknotens
std::vector<DirectedEdge> &adj_from = adj[from];
for (auto it = adj_from.begin(); it != adj_from.end(); ++it) {
if (*it == e) {
adj_from.erase(it);
E--; // Verringern der Gesamtzahl der Kanten
return true;
}
}
// Kante wurde nicht gefunden
return false;
}
/**
* Gibt die Kantenliste eines Knoten v zurueck
*
* \param[in] v Knoten
* \return Vektor mit den Kanten vom Knoten v
*/
const std::vector<DirectedEdge> EdgeWeightedDigraph::operator[](int from) const {
return this->adj[from];
}

56
P4/EdgeWeightedDigraph.h Executable file
View File

@@ -0,0 +1,56 @@
#pragma once
#include <vector>
#include <string>
class DirectedEdge {
private:
int _from; // Startknoten
int _to; // Endknoten
double _weight; // Gewicht der Kante
public:
DirectedEdge()
: _from{0}
, _to{0}
, _weight{0} {
}
DirectedEdge(int from, int to, double weight)
: _weight{weight}
, _from{from}
, _to{to} {
}
double weight() const { return _weight; }
int from() const { return _from; }
int to() const { return _to; }
};
inline bool operator==(const DirectedEdge &lhs, const DirectedEdge &rhs) { return lhs.weight() == rhs.weight(); }
inline bool operator!=(const DirectedEdge &lhs, const DirectedEdge &rhs) { return !operator==(lhs, rhs); }
inline bool operator<(const DirectedEdge &lhs, const DirectedEdge &rhs) { return lhs.weight() < rhs.weight(); }
inline bool operator>(const DirectedEdge &lhs, const DirectedEdge &rhs) { return operator<(rhs, lhs); }
inline bool operator<=(const DirectedEdge &lhs, const DirectedEdge &rhs) { return !operator>(lhs, rhs); }
inline bool operator>=(const DirectedEdge &lhs, const DirectedEdge &rhs) { return !operator<(lhs, rhs); }
class EdgeWeightedDigraph {
private:
int V; // Anzahl Knoten von G
int E; // Anzahl Kanten von G
std::vector<std::vector<DirectedEdge> > adj; // Adjazenzliste
bool validateVertex(int v) const;
void validateVertexWithError(int v) const;
public:
EdgeWeightedDigraph(int V); // Leeren Digraphen mit v Knoten erstellen
EdgeWeightedDigraph(std::string filename); // Graph einlesen aus Textdatei
void add(DirectedEdge e); // gerichtete Kante hinzuf<75>gen
int getV() const; // liefert Anzahl Knoten
int getE() const; // liefert Anzahl der Kanten
std::vector<std::vector<DirectedEdge> > getAdj() const; // liefert die gestamte Kantenliste
std::vector<DirectedEdge> getAdj(int v) const; // liefert Array der adjazenten Kanten zu v
std::vector<DirectedEdge> edges() const; // alle Kanten dieses Graphen
const std::vector<DirectedEdge> operator[](int v) const;
bool del_Edge(DirectedEdge e); // L<>scht eine Kante
};

197
P4/EdgeWeightedGraph.cpp Executable file
View File

@@ -0,0 +1,197 @@
#include "EdgeWeightedGraph.h"
#include <fstream>
#include <assert.h>
#include <iostream>
/**
* Erstellt einen Kantengewichteten-Gaph mit V Knoten
*
* \param[in] V Anzahl der Knoten
*/
EdgeWeightedGraph::EdgeWeightedGraph(int V)
: V{V}
, E{0} {
adj.resize(V);
}
/**
* Erstellt einen Kantengewichteten-Gaph anhand der Kantenliste in fname
*
* \param[in] fname Dateiname der Kantenliste
*/
EdgeWeightedGraph::EdgeWeightedGraph(std::string filename) {
std::ifstream infile(filename);
int tmp_e = 0;
infile >> this->V >> tmp_e;
this->E = 0;
this->adj.resize(this->V, std::vector<Edge>());
int v, w;
double weight;
while (infile >> v >> w >> weight) {
this->add(Edge(v, w, weight));
}
assert(("Missing edges!", tmp_e == this->E));
}
/**
* Erstellt einen Kantengewichteten-Gaph mit V Knoten und den Kanten in edges
*
* \param[in] V Anzahl der Knoten
* \param[in] E Kantenliste
*/
EdgeWeightedGraph::EdgeWeightedGraph(int V, std::vector<Edge> E) {
this->V = V;
this->adj.resize(V);
for (Edge e: E) {
this->add(e);
}
}
/**
* Gibt zurueck ob der Knoten v ein gueltiger Knoten im Graph ist
*
* \param[in] v Knoten
* \return Gueltigkeit des Knoten
*/
bool EdgeWeightedGraph::validateVertex(int v) const {
return v >= 0 && v < this->V;
}
/**
* Gibt eine Fehlermeldung aus, wenn der Knoten v im Graph nicht gueltig ist
*
* \param[in] v Knoten
*/
void EdgeWeightedGraph::validateVertexWithError(int v) const {
assert(("Vertex is out of bounds!", this->validateVertex(v)));
}
/**
* Gibt die Anzahl der Knoten zurueck
*
* \return Anzahl der Knoten
*/
int EdgeWeightedGraph::getV() const {
return this->V;
}
/**
* Gibt die Anzahl der Kanten zurueck
*
* \return Anzahl der Kanten
*/
int EdgeWeightedGraph::getE() const {
return this->E;
}
/**
* Fuegt die Kante e zum Graphen hinzu
*
* \param[in] e neue Kante
*/
void EdgeWeightedGraph::add(Edge e) {
this->validateVertexWithError(e.either());
this->validateVertexWithError(e.other(e.either()));
this->adj[e.either()].push_back(e);
this->adj[e.other(e.either())].push_back(e);
this->E++;
}
/**
* Liefert alle benachbarten Kanten zu v
*
* \param[in] v Knoten von dem aus gesucht wird
* \return Vektor mit allen benachbarten Kanten
*/
std::vector<Edge> EdgeWeightedGraph::getAdj(int v) const {
std::vector<Edge> neighbors;
for (auto const &n: adj[v]) {
neighbors.push_back(n);
}
return neighbors;
}
std::vector<std::vector<Edge> > EdgeWeightedGraph::getAdj() const {
return this->adj;
}
/**
* Gibt alle Kanten im Graph zurueck
*
* \return Vektor mit allen Kanten im Graph
*/
std::vector<Edge> EdgeWeightedGraph::edges() const {
std::vector<Edge> edgeList;
for (int v = 0; v < this->V; v++) {
int selfLoops = 0;
for (Edge e: this->adj[v]) {
if (e.other(v) > v) {
edgeList.push_back(e);
}
// add only one copy of each self loop (self loops will be consecutive)
else if (e.other(v) == v) {
if (selfLoops % 2 == 0) edgeList.push_back(e);
selfLoops++;
}
}
}
return edgeList;
}
bool EdgeWeightedGraph::del_Edge(Edge e) {
int v = e.either();
int w = e.other(v);
// Überprüfen, ob die Knoten gültig sind
if (!validateVertex(v) || !validateVertex(w)) {
return false;
}
bool removed = false;
// Entfernen der Kante von v zu w
std::vector<Edge> &adj_v = adj[v];
for (auto it = adj_v.begin(); it != adj_v.end(); ++it) {
if (*it == e) {
adj_v.erase(it);
removed = true;
break;
}
}
// Entfernen der Kante von w zu v (falls es sich nicht um eine Schlinge handelt)
if (v != w) {
std::vector<Edge> &adj_w = adj[w];
for (auto it = adj_w.begin(); it != adj_w.end(); ++it) {
if (*it == e) {
adj_w.erase(it);
break;
}
}
}
// Wenn die Kante entfernt wurde, verringern wir die Kantenanzahl
if (removed) {
E--;
}
return removed;
}
/**
* Gibt die verbunden Knoten eines Knoten v zurueck
*
* \param[in] v Knoten
* \return Verbundene Knoten des Knoten v
*/
const std::vector<Edge> EdgeWeightedGraph::operator[](int v) const {
this->validateVertexWithError(v);
return this->adj[v];
}

60
P4/EdgeWeightedGraph.h Executable file
View File

@@ -0,0 +1,60 @@
#pragma once
#include <string>
#include <vector>
#include <iomanip>
class Edge {
private:
int _either; // ein Knoten der Kante
int _other; // der andere Knoten der Kante
double _weight; // Kantengewicht
public:
Edge(int either, int other, double weight)
: _either{either}
, _other{other}
, _weight{weight} {
}
double weight() const { return _weight; } // Gewicht dieser Kante
int either() const { return _either; } // einer der beiden Knoten
int other(int vertex) const {
// der von "vertex" verschiedene Knoten
if (vertex == _either) return _other;
else if (vertex == _other) return _either;
else throw new std::invalid_argument("Illegal endpoint");
}
};
inline bool operator==(const Edge &lhs, const Edge &rhs) { return lhs.weight() == rhs.weight(); }
inline bool operator!=(const Edge &lhs, const Edge &rhs) { return !operator==(lhs, rhs); }
inline bool operator<(const Edge &lhs, const Edge &rhs) { return lhs.weight() < rhs.weight(); }
inline bool operator>(const Edge &lhs, const Edge &rhs) { return operator<(rhs, lhs); }
inline bool operator<=(const Edge &lhs, const Edge &rhs) { return !operator>(lhs, rhs); }
inline bool operator>=(const Edge &lhs, const Edge &rhs) { return !operator<(lhs, rhs); }
class EdgeWeightedGraph {
private:
int V; // Anzahl Knoten von G
int E; // Anzahl Kanten von G
std::vector<std::vector<Edge> > adj; // Adjazenzliste
bool validateVertex(int v) const;
void validateVertexWithError(int v) const;
public:
EdgeWeightedGraph(int V); // Leeren Graph mit v Knoten erstellen
EdgeWeightedGraph(std::string filename); // Graph einlesen aus Textdatei
EdgeWeightedGraph(int V, std::vector<Edge> E);
void add(Edge e); // f<>gt Kante e dem Graphen hinzu
int getV() const; // liefert Anzahl Knoten
int getE() const; // liefert Anzahl der Kanten
std::vector<Edge> getAdj(int v) const; // liefert Array der adjazenten Kanten zu v
std::vector<std::vector<Edge> > getAdj() const;
std::vector<Edge> edges() const; // alle Kanten dieses Graphen
bool del_Edge(Edge e);
const std::vector<Edge> operator[](int v) const;
};

417
P4/GraphTest.cpp Executable file
View File

@@ -0,0 +1,417 @@
/*************************************************
* ADS Praktikum 5
* Unit-Testdatei
* Stand: 01.06.2021
*
*************************************************
* Änderungen untersagt!
*************************************************/
#include "catch.hpp"
#include <string>
#include <iostream>
#include "EdgeWeightedGraph.h"
#include "EdgeWeightedDigraph.h"
#include "DijkstraSP.h"
#include "KruskalMST.h"
#include "PrimMST.h"
namespace Graphsearch {
bool DFS(const EdgeWeightedGraph& G, int v, std::vector<bool>& marked, std::vector<int>& edgeTo);
bool BFS(const EdgeWeightedGraph& G, int v, std::vector<bool>& marked, std::vector<int>& edgeTo);
}
/***********************************************
* Testroutine des Graphen:
- Adjazenzlisten
- Tiefensuche mit zusammenhängenden Graphen
- Tiefensuche mit nicht zusammenhängenden Graphen
- Breitensuche mit zusammenhängenden Graphen
- Breitensuche mit nicht zusammenhängenden Graphen
- Prim mit bestimmten Ergebnissen
- Kruskal mit bestimmten Ergebnissen
- Dijkstra
*/
TEST_CASE("Graph Test: Adjazenzliste") {
EdgeWeightedGraph G1("graph1.txt");
EdgeWeightedGraph G2("graph2.txt");
EdgeWeightedGraph G3("graph3.txt");
SECTION("Adjazenzliste des ersten Graphen (graph1.txt)") {
REQUIRE(G1.getV() == 7);
REQUIRE(G1.getE() == 11);
auto adj = G1.getAdj();
REQUIRE(adj[0][0].either() == 0);
REQUIRE(adj[0][0].other(0) == 1);
REQUIRE(adj[0][0].weight() == 7.0);
REQUIRE(adj[4][3].either() == 4);
REQUIRE(adj[4][3].other(4) == 5);
REQUIRE(adj[4][3].weight() == 8.0);
}
SECTION("Adjazenzliste des zweiten Graphen (graph2.txt)") {
REQUIRE(G2.getV() == 20);
REQUIRE(G2.getE() == 60);
auto adj = G2.getAdj();
REQUIRE(adj[0][0].either() == 0);
REQUIRE(adj[0][0].other(0) == 1);
REQUIRE(adj[0][0].weight() == 6.0);
REQUIRE(adj[13][5].either() == 13);
REQUIRE(adj[13][5].other(13) == 19);
REQUIRE(adj[13][5].weight() == 25.0);
}
SECTION("Adjazenzliste des dritten Graphen (graph3.txt)") {
REQUIRE(G2.getV() == 20);
REQUIRE(G2.getE() == 60);
auto adj = G2.getAdj();
REQUIRE(adj[0][0].either() == 0);
REQUIRE(adj[0][0].other(0) == 1);
REQUIRE(adj[0][0].weight() == 6.0);
REQUIRE(adj[13][5].either() == 13);
REQUIRE(adj[13][5].other(13) == 19);
REQUIRE(adj[13][5].weight() == 25.0);
}
}
TEST_CASE("Graph Test: Tiefensuche (DFS)") {
EdgeWeightedGraph G1("graph1.txt");
EdgeWeightedGraph G2("graph2.txt");
EdgeWeightedGraph G3("graph3.txt");
std::vector<bool> marked;
std::vector<int> edgeTo;
SECTION("Tiefensuche mit erstem Graphen (graph1.txt") {
marked.resize(42, false); // Dummy values, damit die unit-Tests mit
edgeTo.resize(42, -1); // der Vorlage durchlaufen ohne Programmabsturz
for (int i = 0; i < G1.getV(); i++) {
bool connected = Graphsearch::DFS(G1, i, marked, edgeTo);
REQUIRE(connected == true);
}
// edgeTo-Array der Tiefensuche mit Startknoten 0 testen
Graphsearch::DFS(G1, 0, marked, edgeTo);
REQUIRE(edgeTo.size() == G1.getV());
REQUIRE(marked.size() == G1.getV());
REQUIRE(edgeTo[0] == -1);
REQUIRE(edgeTo[1] == 0);
REQUIRE(edgeTo[2] == 1);
REQUIRE(edgeTo[3] == 4);
REQUIRE(edgeTo[4] == 2);
REQUIRE(edgeTo[5] == 3);
REQUIRE(edgeTo[6] == 5);
}
SECTION("Tiefensuche mit zweiten Graphen (graph2.txt)") {
marked.resize(42, false); // Dummy values, damit die unit-Tests mit
edgeTo.resize(42, -1); // der Vorlage durchlaufen ohne Programmabsturz
for (int i = 0; i < G2.getV(); i++) {
bool connected = Graphsearch::DFS(G2, i, marked, edgeTo);
REQUIRE(connected == true);
}
Graphsearch::DFS(G2, 0, marked, edgeTo);
REQUIRE(edgeTo.size() == G2.getV());
REQUIRE(marked.size() == G2.getV());
REQUIRE(edgeTo[0] == -1);
REQUIRE(edgeTo[1] == 0);
REQUIRE(edgeTo[7] == 4);
REQUIRE(edgeTo[8] == 9);
REQUIRE(edgeTo[19] == 13);
}
SECTION("Tiefensuche mit dritten Graphen (graph3.txt)") {
marked.resize(42, false); // Dummy values, damit die unit-Tests mit
edgeTo.resize(42, -1); // der Vorlage durchlaufen ohne Programmabsturz
for (int i = 0; i < G3.getV(); i++) {
bool connected = Graphsearch::DFS(G3, i, marked, edgeTo);
REQUIRE(connected == false);
}
Graphsearch::DFS(G3, 0, marked, edgeTo);
REQUIRE(edgeTo.size() == G3.getV());
REQUIRE(marked.size() == G3.getV());
REQUIRE(marked[0] == true);
REQUIRE(marked[4] == true);
REQUIRE(marked[18] == true);
Graphsearch::DFS(G3, 1, marked, edgeTo);
REQUIRE(edgeTo.size() == G3.getV());
REQUIRE(marked.size() == G3.getV());
REQUIRE(marked[0] == false);
REQUIRE(marked[4] == false);
REQUIRE(marked[18] == false);
}
}
TEST_CASE("Graph Test: Breitensuche (BFS)") {
EdgeWeightedGraph G1("graph1.txt");
EdgeWeightedGraph G2("graph2.txt");
EdgeWeightedGraph G3("graph3.txt");
std::vector<bool> marked;
std::vector<int> edgeTo;
SECTION("Breitensuche mit erstem Graphen (graph1.txt") {
marked.resize(42, false); // Dummy values, damit die unit-Tests mit
edgeTo.resize(42, -1); // der Vorlage durchlaufen ohne Programmabsturz
for (int i = 0; i < G1.getV(); i++) {
bool connected = Graphsearch::BFS(G1, i, marked, edgeTo);
REQUIRE(connected == true);
}
// edgeTo-Array der Breitensuche mit Startknoten 0 testen
Graphsearch::BFS(G1, 0, marked, edgeTo);
REQUIRE(edgeTo.size() == G1.getV());
REQUIRE(marked.size() == G1.getV());
REQUIRE(edgeTo[0] == -1);
REQUIRE(edgeTo[1] == 0);
REQUIRE(edgeTo[2] == 1);
REQUIRE(edgeTo[3] == 0);
REQUIRE(edgeTo[4] == 1);
REQUIRE(edgeTo[5] == 3);
REQUIRE(edgeTo[6] == 4);
}
SECTION("Breitensuche mit zweiten Graphen (graph2.txt)") {
marked.resize(42, false); // Dummy values, damit die unit-Tests mit
edgeTo.resize(42, -1); // der Vorlage durchlaufen ohne Programmabsturz
for (int i = 0; i < G2.getV(); i++) {
bool connected = Graphsearch::BFS(G2, i, marked, edgeTo);
REQUIRE(connected == true);
}
// edgeTo-Array der Breitensuche mit Startknoten 0 testen
Graphsearch::BFS(G2, 0, marked, edgeTo);
REQUIRE(edgeTo.size() == G2.getV());
REQUIRE(marked.size() == G2.getV());
REQUIRE(edgeTo[0] == -1);
REQUIRE(edgeTo[1] == 0);
REQUIRE(edgeTo[2] == 3);
REQUIRE(edgeTo[6] == 16);
REQUIRE(edgeTo[11] == 4);
REQUIRE(edgeTo[15] == 4);
REQUIRE(edgeTo[19] == 0);
}
SECTION("Breitensuche mit dritten Graphen (graph3.txt)") {
marked.resize(42, false); // Dummy values, damit die unit-Tests mit
edgeTo.resize(42, -1); // der Vorlage durchlaufen ohne Programmabsturz
for (int i = 0; i < G3.getV(); i++) {
bool connected = Graphsearch::BFS(G3, i, marked, edgeTo);
REQUIRE(edgeTo.size() == G3.getV());
REQUIRE(marked.size() == G3.getV());
REQUIRE(connected == false);
// Hinweis: Die Knoten {0,4,18} bilden einen Teilgraphen
if (i == 0 || i == 4 || i == 18) {
REQUIRE(marked[0] == true);
REQUIRE(marked[4] == true);
REQUIRE(marked[18] == true);
}
else {
REQUIRE(marked[0] == false);
REQUIRE(marked[4] == false);
REQUIRE(marked[18] == false);
}
}
}
}
TEST_CASE("Graph Test: Prim") {
EdgeWeightedGraph G1("graph1.txt");
EdgeWeightedGraph G2("graph2.txt");
EdgeWeightedGraph G3("graph3.txt");
SECTION("Prim mit erstem Graph (graph1.txt)") {
for (int i = 0; i < G1.getV(); i++) {
PrimMST prim(G1, i);
REQUIRE(prim.weight() == 39.0);
auto edges = prim.edges();
REQUIRE(edges.size() == 6);
std::vector<Edge> mst;
mst.push_back(Edge(0, 3, 5.0));
mst.push_back(Edge(2, 4, 5.0));
mst.push_back(Edge(3, 5, 6.0));
mst.push_back(Edge(1, 4, 7.0));
mst.push_back(Edge(0, 1, 7.0));
mst.push_back(Edge(4, 6, 9.0));
// Testen ob alle Edes des MST exitieren
for (auto const& e : edges) {
REQUIRE(std::find(mst.begin(), mst.end(), e) != mst.end());
}
}
}
SECTION("Prim mit zweitem Graph (graph2.txt)") {
for (int i = 0; i < G2.getV(); i++) {
PrimMST prim(G2, i);
REQUIRE(prim.weight() == 150.0);
auto edges = prim.edges();
REQUIRE(edges.size() == 19);
}
}
SECTION("Prim mit drittem Graphen (graph3.txt)") {
// Graph 3 mit Strartknoten 0
PrimMST prim_0(G3, 0);
REQUIRE(prim_0.weight() == 16.0);
// Graph 3 mit Strartknoten 16
PrimMST prim_16(G3, 16);
REQUIRE(prim_16.weight() == 168.0);
}
}
TEST_CASE("Graph Test: Kruskal") {
EdgeWeightedGraph G1("graph1.txt");
EdgeWeightedGraph G2("graph2.txt");
EdgeWeightedGraph G3("graph3.txt");
SECTION("Kruskal mit erstem Graph (graph1.txt)") {
KruskalMST kruskal(G1);
REQUIRE(kruskal.weight() == 39.0);
auto edges = kruskal.edges();
REQUIRE(edges.size() == 6);
std::vector<Edge> mst;
mst.push_back(Edge(0, 3, 5.0));
mst.push_back(Edge(2, 4, 5.0));
mst.push_back(Edge(3, 5, 6.0));
mst.push_back(Edge(1, 4, 7.0));
mst.push_back(Edge(0, 1, 7.0));
mst.push_back(Edge(4, 6, 9.0));
// Testen ob alle Edes des MST exitieren
for (auto const& e : edges) {
REQUIRE(std::find(mst.begin(), mst.end(), e) != mst.end());
}
}
SECTION("Kruskal mit zweitem Graph (graph2.txt)") {
KruskalMST kruskal(G2);
REQUIRE(kruskal.weight() == 150.0);
REQUIRE(kruskal.edges().size() == 19);
}
SECTION("Kruskal mit drittem Graph (graph3.txt)") {
KruskalMST kruskal(G3);
REQUIRE(kruskal.weight() == 184.0);
REQUIRE(kruskal.edges().size() == 18);
}
}
TEST_CASE("Graph Test: Dijkstra") {
EdgeWeightedDigraph G1_directed("graph1.txt");
EdgeWeightedDigraph G2_directed("graph2.txt");
EdgeWeightedDigraph G3_directed("graph3.txt");
int start, target;
SECTION("Dijkstra beim ersten Graph - 0 nach 6 (graph1.txt)") {
start = 0;
target = 6;
DijkstraSP dsp(G1_directed, start);
auto path = dsp.pathTo(target);
REQUIRE(dsp.distTo(target) == 22.0);
REQUIRE(path.size() == 3);
REQUIRE(path[0] == DirectedEdge(0, 3, 5.0));
REQUIRE(path[1] == DirectedEdge(3, 5, 6.0));
REQUIRE(path[2] == DirectedEdge(5, 6, 11.0));
}
SECTION("Dijkstra beim ersten Graph - 2 nach 6 (graph1.txt)") {
start = 2;
target = 6;
DijkstraSP dsp(G1_directed, start);
auto path = dsp.pathTo(target);
REQUIRE(dsp.distTo(target) == 14.0);
REQUIRE(path.size() == 2);
REQUIRE(path[0] == DirectedEdge(2, 4, 5.0));
REQUIRE(path[1] == DirectedEdge(4, 6, 9.0));
}
SECTION("Dijkstra beim zweiten Graph - 0 nach 6 (graph2.txt)") {
start = 0;
target = 6;
DijkstraSP dsp(G2_directed, start);
auto path = dsp.pathTo(target);
REQUIRE(dsp.distTo(target) == 91.0);
REQUIRE(path.size() == 3);
REQUIRE(path[0] == DirectedEdge(0, 4, 47.0));
REQUIRE(path[1] == DirectedEdge(4, 5, 36.0));
REQUIRE(path[2] == DirectedEdge(5, 6, 8.0));
}
SECTION("Dijkstra beim zweiten Graph - 3 nach 9 (graph2.txt)") {
start = 3;
target = 9;
DijkstraSP dsp(G2_directed, start);
auto path = dsp.pathTo(target);
REQUIRE(dsp.distTo(target) == 122.0);
REQUIRE(path.size() == 4);
REQUIRE(path[0] == DirectedEdge(3, 4, 46.0));
REQUIRE(path[1] == DirectedEdge(4, 5, 36.0));
REQUIRE(path[2] == DirectedEdge(5, 6, 8.0));
REQUIRE(path[3] == DirectedEdge(6, 9, 32.0));
}
SECTION("Dijkstra beim dritten Graph - 0 nach 4 (graph3.txt)") {
start = 0;
target = 4;
DijkstraSP dsp(G3_directed, start);
auto path = dsp.pathTo(target);
REQUIRE(dsp.distTo(target) == 47.0);
REQUIRE(path.size() == 1);
REQUIRE(path[0] == DirectedEdge(0, 4, 47.0));
}
SECTION("Dijkstra beim dritten Graph - 1 nach 16 (graph3.txt)") {
start = 1;
target = 16;
DijkstraSP dsp(G3_directed, start);
auto path = dsp.pathTo(target);
REQUIRE(dsp.distTo(target) == 35.0);
REQUIRE(path.size() == 2);
REQUIRE(path[0] == DirectedEdge(1, 7, 9.0));
REQUIRE(path[1] == DirectedEdge(7, 16, 26.0));
}
SECTION("Dijkstra beim dritten Graph - 3 nach 7 (graph3.txt)") {
start = 3;
target = 7;
DijkstraSP dsp(G3_directed, start);
auto path = dsp.pathTo(target);
REQUIRE(dsp.distTo(target) == 42.0);
REQUIRE(path.size() == 3);
REQUIRE(path[0] == DirectedEdge(3, 5, 27.0));
REQUIRE(path[1] == DirectedEdge(5, 6, 8.0));
REQUIRE(path[2] == DirectedEdge(6, 7, 7.0));
}
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// http://www.hashemall.com/
// Zeile 1 - 413 hat den SHA 256 Hashwert: C812B068B2223D95AC8750EBBEB14261A7DFA84DF2DC4BC8F0026E7223C35A37

101
P4/Graphsearch.h Executable file
View File

@@ -0,0 +1,101 @@
#pragma once
#include "EdgeWeightedDigraph.h"
#include "EdgeWeightedGraph.h"
#include <iostream>
namespace Graphsearch {
/**
* Fuehrt eine rekursive Tiefensuche im Graphen G,
* ab dem Knoten v aus und markiert alle besuchten
* Knoten in marked.
* Alle besuchten Knoten werden ausgegeben.
*
* \param[in] G Graph
* \param[in] v Startknoten
* \param[in/out] marked Bereits besuchte Knoten
* \param[in/out] edgeTo Vektor mit dem Nachfolgeknoten zu jedem Knoten
*/
void DFS_recursive(const EdgeWeightedGraph &G, int v, std::vector<bool> &marked, std::vector<int> &edgeTo) {
std::cout << v;
marked[v] = true;
for (int j = 0; j < G.getAdj(v).size(); j++) {
int w = G.getAdj(v)[j].other(v);
if (marked[w] == false) {
edgeTo[w] = v;
std::cout << " -> ";
DFS_recursive(G, w, marked, edgeTo);
}
}
}
/**
* Fuehrt eine rekursive Tiefensuche im Graphen g, ab dem Knoten v aus.
* Alle besuchten Knoten werden ausgegeben.
* Starterfunktion zu DFS_recursive(EdgeWeigtedGraph, int, std::vector<bool>, std::vector<int>)
*
* \param[in] G Graph
* \param[out] marked Bereits besuchte Knoten
* \param[out] edgeTo Vektor mit dem Nachfolgeknoten zu jedem Knoten
* \param[in] v Startknoten
* \return true Graph ist zusammenhaengend
* false Graph ist nicht zusammenhaengend
*/
bool DFS(const EdgeWeightedGraph &G, int v, std::vector<bool> &marked, std::vector<int> &edgeTo) {
bool ret = true;
marked.clear();
marked.resize(G.getV(), false);
edgeTo.clear();
edgeTo.resize(G.getV(), -1);
DFS_recursive(G, v, marked, edgeTo);
std::cout << std::endl;
for (bool value: marked)
ret = ret && value;
return ret;
}
/**
* Fuehrt eine iterative Breitensuche im Graphen g, ab dem Knoten v aus.
* Alle besuchten Knoten werden ausgegeben.
*
* \param[in] G Graph
* \param[in] v Startknoten
* \param[out] marked Gibt an welche Knoten besucht wurden bei der Suche
* \param[out] edgeTo Gibt den Nachfolgerknoten eines Knoten an
* \return true Graph ist zusammenhaengend
* false Graph ist nicht zusammenhaengend
*/
bool BFS(const EdgeWeightedGraph &G, int v, std::vector<bool> &marked, std::vector<int> &edgeTo) {
marked.clear();
marked.resize(G.getV(), false);
edgeTo.clear();
edgeTo.resize(G.getV(), -1);
std::queue<int> q;
marked[v] = true;
q.push(v);
while (!q.empty()) {
int currentV = q.front();
q.pop();
std::cout << currentV << " "; // Knoten besuchen (hier: ausgeben)
for (const Edge &edge: G.getAdj(currentV)) {
int w = edge.other(currentV);
if (!marked[w]) {
edgeTo[w] = currentV;
marked[w] = true;
q.push(w);
}
}
}
// <20>berpr<70>fen, ob alle Knoten besucht wurden
for (bool m: marked) {
if (!m) return false;
}
return true;
}
}

59
P4/KruskalMST.cpp Executable file
View File

@@ -0,0 +1,59 @@
#include "KruskalMST.h"
/**
* Erstellt einen MST zum Graph G mit dem Kruskal Algorithmus
*
* \param[in] G Kantengewichteter-Graph
*/
KruskalMST::KruskalMST(EdgeWeightedGraph G) {
treeID.resize(G.getV());
std::priority_queue<Edge, std::vector<Edge>, std::greater<Edge> > pq;
std::vector<Edge> e = G.edges(); // liefert alle Kanten von G
for (int i = 0; i < e.size(); i++)
pq.push(e[i]);
// zu Beginn ist jeder Knoten ein eigener Baum
for (int i = 0; i < G.getV(); i++)
treeID[i] = i;
while (!pq.empty()) {
Edge e = pq.top();
pq.pop();
int v = e.either();
int w = e.other(v);
// Wenn Knoten v und w zu unterschiedlichen B?umen geh?rt,
// k<>nnen diese mit der Kante e ohne Zykel verbunden werden
if (treeID[v] != treeID[w]) {
mst.push_back(e);
int treeID_w = treeID[w];
for (int i = 0; i < G.getV(); i++) {
// BaumID von v und w
if (treeID[i] == treeID_w) // angleichen
treeID[i] = treeID[v];
}
}
}
}
/**
* Gibt alle Kanten vom MST zurueck
*
* \return Vektor mit Kanten des MST
*/
std::vector<Edge> KruskalMST::edges() const {
return mst;
}
/**
* Gibt die Summe aller Gewichte im MST zurueck
*
* \return Summe der Gewichte im MST
*/
double KruskalMST::weight() const {
double gew = 0.0;
for (Edge e: this->mst)
gew += e.weight();
return gew;
}

18
P4/KruskalMST.h Executable file
View File

@@ -0,0 +1,18 @@
#pragma once
#include <vector>
#include <queue>
#include "EdgeWeightedGraph.h"
class KruskalMST {
private:
std::vector<Edge> mst; // MST-Kanten
std::vector<int> treeID; // BaumId zu jedem Knoten
public:
KruskalMST() {
};
KruskalMST(EdgeWeightedGraph G);
std::vector<Edge> edges() const; // liefert MST
double weight() const; // berechnet Gesamtkosten des MST
};

70
P4/PrimMST.cpp Executable file
View File

@@ -0,0 +1,70 @@
#include "PrimMST.h"
/**
* Erstellt einen MST zum Graph G mit dem Prim Algorithmus
*
* \param[in] G Kantengewichteter-Graph
* \param[in] s Startknoten
*/
PrimMST::PrimMST(EdgeWeightedGraph G, int s) {
marked.resize(G.getV(), false);
// lege alle Kanten vom Startknoten 0 ausgehend in die Priority Queue (PQ)
// setzt voraus, dass G zusammenhaengend ist
visit(G, s);
while (!pq.empty()) {
Edge e = pq.top(); // Hole Kante mit geringstem Gewicht aus PQ
pq.pop(); // entferne diese Kante aus PQ
int v = e.either(); // Knoten 1 der Kante
int w = e.other(v); // Knoten 2 der Kante
// <20>berspringen, falls beide Knoten im Baum markiert sind
if (marked[v] && marked[w])
continue; // Zykel-Detektion
mst.push_back(e); // F<>ge Kante e zum MST hinzu
if (!marked[v])
visit(G, v); // Knoten v oder w zum MSP
if (!marked[w])
visit(G, w); // hinzuf?gen
}
}
/**
* Markiert Knoten v im Graph G als markiert und fuegt alle Nachbarn zur pq hinzu
*
* \param[in] G Kantengewichteter-Graph
* \param[in] v Knoten im Graph G
*/
void PrimMST::visit(EdgeWeightedGraph G, int v) {
marked[v] = true;
std::vector<Edge> Tedges = G[v]; // liefert alle Kanten ausgehend vom Knoten v
// Lege alle Kanten von v zu unmarkierten
// (noch nicht besuchten) Knoten in die PQ ab
for (int i = 0; i < Tedges.size(); i++) {
if (!marked[Tedges[i].other(v)])
pq.push(Tedges[i]);
}
}
/**
* Gibt alle Kanten vom MST zurueck
*
* \return Vektor mit Kanten des MST
*/
std::vector<Edge> PrimMST::edges() const {
return mst;
}
/**
* Gibt die Summe aller Gewichte im MST zurueck
*
* \return Summe der Gewichte im MST
*/
double PrimMST::weight() const {
double gew = 0.0;
for (Edge e: this->mst) {
gew += e.weight();
}
return gew;
}

28
P4/PrimMST.h Executable file
View File

@@ -0,0 +1,28 @@
#pragma once
#include <vector>
#include <queue>
#include "EdgeWeightedGraph.h"
class PrimMST {
private:
std::vector<bool> marked; // MST-Knoten
std::vector<Edge> mst; // MST-Kanten
/*
* PriorityQueue die alle Kanten speichert und mit pq.top()
* die Kante mit dem kleinsten Gewicht zurueck gibt.
*/
std::priority_queue<Edge, std::vector<Edge>, std::greater<Edge> > pq;
public:
PrimMST() {
}
PrimMST(EdgeWeightedGraph G, int s);
void visit(EdgeWeightedGraph G, int v);
std::vector<Edge> edges() const;
double weight() const;
};

152
P4/PriorityQueue.h Executable file
View File

@@ -0,0 +1,152 @@
#pragma once
#include <queue>
#include <vector>
#include <map>
#include <ostream>
namespace Utils {
/**
* Wrapper-Klasse um Vergleiche unabhaengig von value zu ermoeglichen
*
* \tparam[in] _CompareValueTyupe Typ der compareValue
* \tparam[in] _ValueType Typ der value
*/
template<class _CompareValueTyupe, class _ValueType>
class CompareContainer {
public:
_CompareValueTyupe compareValue;
_ValueType value;
/**
* Konstruktor
*
* \param[in] compareValue Anhand dieser Variable wird verglichen
* \param[in] value Variable fuer die ein Vergleich benoetigt wird
*/
CompareContainer(_CompareValueTyupe compareValue, _ValueType value) : compareValue{compareValue}, value{value} {
}
/**
* Vergleicht zwei CompareContainer
*
* \param[in] other Container 2
* \return true, wenn die compareValue von this und other gleich ist. Ansonsten false
*/
bool operator==(const CompareContainer &other) const { return compareValue == other.compareValue; }
/**
* Vergleicht zwei CompareContainer
*
* \param[in] other Container 2
* \return true, wenn die compareValue von this und other ungleich ist. Ansonsten false
*/
bool operator!=(const CompareContainer &other) const { return !this->operator==(other); }
/**
* Vergleicht zwei CompareContainer
*
* \param[in] other Container 2
* \return true, wenn die compareValue von this kleiner als die von other ist. Ansonsten false
*/
bool operator<(const CompareContainer &other) const { return compareValue < other.compareValue; }
/**
* Vergleicht zwei CompareContainer
*
* \param[in] other Container 2
* \return true, wenn die compareValue von this groesser als die von other ist. Ansonsten false
*/
bool operator>(const CompareContainer &other) const {
return !this->operator<(other) && !this->operator==(other);
}
/**
* Vergleicht zwei CompareContainer
*
* \param[in] other Container 2
* \return true, wenn die compareValue von this kleiner oder gleich, wie die von other ist. Ansonsten false
*/
bool operator<=(const CompareContainer &other) const { return !this->operator>(other); }
/**
* Vergleicht zwei CompareContainer
*
* \param[in] other Container 2
* \return true, wenn die compareValue von this groesser oder gleich, wie die von other ist. Ansonsten false
*/
bool operator>=(const CompareContainer &other) const { return !this->operator<(other); }
};
/**
* Wrapper-Klasse um die Funktionsweise der std::priority_queue zu erweitern. Ermoeglicht es Objekte mit einem unabhaengigen Gewicht in die pq zu laden und erweitert die pq mit weiteren Methoden.
* Das kleinste Gewicht ist immer an Position top()
*
* \tparam[in] _Tr Datentyp der PriorityQueue
*/
template<typename _Tr>
class PriorityQueue : public std::priority_queue<CompareContainer<double, _Tr>, std::vector<CompareContainer<double,
_Tr> >, std::greater<CompareContainer<double, _Tr> > > {
public:
/**
* Fuegt ein item mit dem Gewicht weight zur pq hinzu.
*
* \param[in] item Objekt was zur pq hinzugefuegt werden soll
* \param[in] weight Das Gewicht mit dem item priorisiert werden soll
*/
void push(_Tr item, double weight) {
std::priority_queue<CompareContainer<double, _Tr>, std::vector<CompareContainer<double, _Tr> >, std::greater
<CompareContainer<double, _Tr> > >::push(CompareContainer<double, _Tr>(weight, item));
}
/**
* Gibt das Item an Position top() zurueck und entfernt dieses aus der pq.
*
* \return Item an Position top()
*/
_Tr pop_top() {
_Tr item = this->top().value;
this->pop();
return item;
}
/**
* Entfernt element aus der pq und gibt zurueck, ob es gefunden und entfernt wurde.
*
* \return true, wenn element aus der pq entfernt wurde, sonst false
*/
bool remove(const _Tr &element) {
for (auto it = this->c.begin(); it != this->c.end(); it++) {
if ((*it).value == element) {
this->c.erase(it);
return true;
}
}
return false;
}
/**
* Sucht nach element in der pq und gibt zurueck ob es gefunden wurde
*
* \return true, wenn element in der pq gefunden wurde, sonst false
*/
bool contains(const _Tr &element) {
for (auto it = this->c.begin(); it != this->c.end(); it++) {
if ((*it).value == element) {
return true;
}
}
return false;
}
/**
* Tauscht das Gewicht von element aus und gibt zurueck ob es gefunden wurde.
*
* \return true, wenn element in der pq gefunden wurde, sonst false
*/
bool change(const _Tr &element, double weight) {
if (!remove(element)) return false;
this->push(element, weight);
return true;
}
};
}

18182
P4/catch.h Executable file

File diff suppressed because it is too large Load Diff

18182
P4/catch.hpp Executable file

File diff suppressed because it is too large Load Diff

13
P4/graph1.txt Executable file
View File

@@ -0,0 +1,13 @@
7
11
0 1 7
0 3 5
1 2 8
1 3 9
1 4 7
2 4 5
3 4 15
3 5 6
4 5 8
4 6 9
5 6 11

62
P4/graph2.txt Executable file
View File

@@ -0,0 +1,62 @@
20
60
0 1 6
0 4 47
0 8 11
0 16 19
0 19 1
1 3 4
1 7 9
1 10 21
1 13 33
1 16 42
2 3 5
2 5 15
2 12 24
2 13 37
2 17 39
3 4 46
3 11 16
3 18 32
4 5 36
4 7 39
4 11 40
4 15 49
4 18 4
5 6 8
5 11 16
5 12 19
5 16 21
5 18 30
6 7 7
6 9 32
6 13 33
6 16 36
6 17 42
7 8 49
7 11 6
7 15 11
7 16 26
7 17 32
8 9 38
8 12 47
8 14 3
8 18 6
9 10 21
9 14 27
9 19 38
10 13 40
10 15 3
10 19 15
11 14 17
11 17 30
11 19 38
12 15 39
12 17 6
13 18 9
14 15 17
13 19 25
15 16 31
15 19 42
16 18 11
17 19 24

45
P4/graph3.txt Executable file
View File

@@ -0,0 +1,45 @@
20
43
0 4 47
0 18 12
1 3 4
1 7 9
1 10 21
1 13 33
1 16 42
2 3 5
2 12 24
2 13 37
2 17 39
3 5 27
3 8 17
3 11 16
3 12 5
4 18 4
5 6 8
5 11 16
5 12 19
5 16 21
6 7 7
6 13 33
6 16 36
6 17 42
7 8 49
7 11 6
7 16 26
7 17 32
8 12 47
8 14 3
8 16 46
9 10 21
9 19 38
10 15 3
10 19 15
11 14 17
11 13 32
11 17 30
12 14 26
12 17 6
13 16 35
14 17 2
15 19 42

296
P4/main.cpp Executable file
View File

@@ -0,0 +1,296 @@
#define CATCH_CONFIG_RUNNER
#include "catch.h"
#include <iostream>
#include "EdgeWeightedGraph.h"
#include "PrimMST.h"
#include "Graphsearch.h"
#include "KruskalMST.h"
#include "DijkstraSP.h"
using namespace std;
bool isInGrapf(EdgeWeightedGraph gr, int v) {
if (v > gr.getV())
return false;
return true;
}
void PrintGrapf(EdgeWeightedGraph g) {
vector<vector<Edge> > adj = g.getAdj();
cout << endl;
for (int i = 0; i < g.getV(); i++) {
cout << i << " -> ";
for (int j = 0; j < adj[i].size(); j++) {
cout << adj[i][j].other(i) << "[" << adj[i][j].weight() << "]";
if (j < adj[i].size() - 1) {
cout << " -> ";
}
}
cout << endl;
}
}
void print_digraph(EdgeWeightedDigraph graph) {
int prev = -1;
for (DirectedEdge e: graph.edges()) {
if (e.from() != prev)
cout << endl << e.from();
cout << " -> " << e.to() << " [" << e.weight() << "] ";
prev = e.from();
}
cout << endl << endl;
}
void print_mst(vector<Edge> mst) {
vector<int> v;
cout << endl;
for (Edge e: mst) {
int vorhanden = 0;
for (int i: v) {
if (e.either() == i)
vorhanden++;
}
if (vorhanden == 0) {
cout << e.either();
for (Edge z: mst) {
if (z.either() == e.either()) {
cout << " -> " << z.other(z.either()) << " [" << z.weight() << "] ";
}
}
cout << endl;
}
v.push_back(e.either());
}
}
void search(EdgeWeightedGraph gra, int type) {
int search;
cout << endl << "Suche nach ?> ";
cin >> search;
vector<bool> marked;
vector<int> edgeTo;
bool zsm;
if (!isInGrapf(gra, search))
cout << search << " ist nicht im Graphen" << endl;
if (type == 2) {
cout << "Tiefensuche(Depth - First - Search(DFS)) - Startknoten: " << search << endl << "Besuchsreihenfolge :"
<< endl;
zsm = Graphsearch::DFS(gra, search, marked, edgeTo);
} else {
cout << "Breitensuche(Breadth - First - Search(BFS)) - Startknoten: " << search << endl <<
"Besuchsreihenfolge :"
<< endl;
zsm = Graphsearch::BFS(gra, search, marked, edgeTo);
}
cout << endl << "Marked Array:";
for (int i = 0; i < marked.size(); i++)
cout << endl << i << " -> " << marked[i];
cout << endl << "Edge To Array:";
for (int i = 0; i < edgeTo.size(); i++)
cout << endl << i << " -> " << edgeTo[i];
if (zsm)
cout << endl << "Graph ist zusammenhaengend" << endl;
else
cout << endl << "Graph ist nicht zusammenhaengend" << endl;
}
bool get_edge_values(EdgeWeightedGraph &gra, int &from, int &to) {
cout << endl << "Start Knoten ?>";
cin >> from;
if (!isInGrapf(gra, from)) {
cout << from << " ist nicht im Graphen" << endl;
return false;
}
cout << endl << "Ziel Knoten ?>";
cin >> to;
if (!isInGrapf(gra, to)) {
cout << to << " ist nicht im Graphen" << endl;
return false;
}
return true;
}
int main() {
// Starte Unit-Tests
Catch::Session().run();
int selection;
string graph;
EdgeWeightedGraph gra(graph);
EdgeWeightedDigraph wgra(graph);
while (true) {
if (graph == "")
cout << endl << "Graph nicht gefunden" << endl;
cout << endl
<< "Praktikum 4: Graphenalgorithem:" << endl
<< "1) Graph einlesen" << endl
<< "2) Tiefensuche" << endl
<< "3) Breitensuche" << endl
<< "4) MST nach Prim" << endl
<< "5) MST nach Kruskal" << endl
<< "6) Kuerzeste Wege nach Dijkstra" << endl
<< "7) Ausgabe der Adjazenzliste" << endl
<< "8) Kante löschen" << endl
<< "9) Kante hinzufügen" << endl
<< "10) Programm beenden" << endl
<< "? > ";
cin >> selection;
switch (selection) {
case 1: {
int auswahl;
cout << endl << "Graph einlesen" << endl
<< "1) Graph1" << endl
<< "2) Graph2" << endl
<< "3) Graph3" << endl
<< "?> ";
cin >> auswahl;
switch (auswahl) {
case 1:
graph = "graph1.txt";
break;
case 2:
graph = "graph2.txt";
break;
case 3:
graph = "graph3.txt";
break;
}
if (graph != "") {
gra = EdgeWeightedGraph(graph);
wgra = EdgeWeightedDigraph(graph);
cout << endl << "Graph " << auswahl << " wurde eingelesen" << endl;
} else
cout << endl << "Graph nicht gefunden" << endl;
break;
}
case 2: {
search(gra, selection);
break;
}
case 3: {
search(gra, selection);
break;
}
case 4: {
int start = 0;
cout << endl << "Start Knoten ?> ";
cin >> start;
if (!isInGrapf(gra, start)) {
cout << start << " ist nicht im Graphen" << endl;
break;
}
PrimMST test(gra, start);
cout << "Minimaler Spannbaum(MST) nach Prim :" << endl;
cout << "Gewicht: " << test.weight();
vector<Edge> mst = test.edges();
print_mst(mst);
break;
}
case 5: {
KruskalMST test(gra);
cout << "Minimaler Spannbaum(MST) nach Kruskal :" << endl;
cout << "Gewicht: " << test.weight();
vector<Edge> mst = test.edges();
print_mst(mst);
break;
}
case 6: {
int start, ziel = 0;
cout << endl << "Start Knoten ?> ";
cin >> start;
if (!isInGrapf(gra, start)) {
cout << start << " ist nicht im Graphen" << endl;
continue;
}
cout << endl << "Ziel Knoten ?>";
cin >> ziel;
if (!isInGrapf(gra, ziel)) {
cout << ziel << " ist nicht im Graphen" << endl;
continue;
}
DijkstraSP dijkstra(wgra, start);
vector<DirectedEdge> path = dijkstra.pathTo(ziel);
print_digraph(wgra);
double kosten = 0.0;
string out = "";
cout << "Pfad: ";
for (DirectedEdge de: path) {
kosten += de.weight();
out += to_string(de.from()) + " [" + to_string(de.weight()) + "] " + " -> ";
}
if (kosten == 0.0) {
cout << "Kein Weg gefunden" << endl;
continue;
}
cout << out;
cout << path[path.size() - 1].to() << endl;
cout << "Kosten: " << kosten << endl;
break;
}
case 7: {
PrintGrapf(gra);
break;
}
case 8: {
int from, to;
if (get_edge_values(gra, from, to)) {
Edge n(from, to, 0);
DirectedEdge m(from, to, 0);
gra.del_Edge(n);
wgra.del_Edge(m);
cout << "Die Kante (" << from << " ; " << to << ") wurde gelöscht." << endl;
break;
}
break;
}
case 9: {
int from, to, weight;
bool a = get_edge_values(gra, from, to);
cout << endl << "Gewicht ?>";
cin >> weight;
if (a) {
Edge n(from, to, weight);
DirectedEdge m(from, to, weight);
gra.add(n);
wgra.add(m);
cout << "Die Kante (" << from << " ; " << to << " ; " << weight << ") wurde hinzugefügt." << endl;
}
break;
}
case 10: {
}
}
}
return 0;
}