Skip to content

Commit 4eadc13

Browse files
committed
upload files
0 parents  commit 4eadc13

13 files changed

+8220
-0
lines changed

.gitignore

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Prerequisites
2+
*.d
3+
4+
# Compiled Object files
5+
*.slo
6+
*.lo
7+
*.o
8+
*.obj
9+
10+
# Precompiled Headers
11+
*.gch
12+
*.pch
13+
14+
# Compiled Dynamic libraries
15+
*.so
16+
*.dylib
17+
*.dll
18+
19+
# Fortran module files
20+
*.mod
21+
*.smod
22+
23+
# Compiled Static libraries
24+
*.lai
25+
*.la
26+
*.a
27+
*.lib
28+
29+
# Executables
30+
*.exe
31+
*.out
32+
*.app

ASSIGNMENT.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# מטלה 1 - גרפים (Classes and Namespaces)
2+
3+
המטרה שלכם במטלה הזאת היא ליצור מחלקה שמייצגת גרף ולממש אלגוריתמים על הגרפים (זה הזמן להזכר בקורס אלגוריתמים 1).
4+
5+
במטלה הזאת הייצוג של הגרף שלכם יתבצע בעזרת מטריצת שכנויות - https://he.wikipedia.org/wiki/%D7%9E%D7%98%D7%A8%D7%99%D7%A6%D7%AA_%D7%A9%D7%9B%D7%A0%D7%95%D7%AA.
6+
7+
הגרף יכול להיות גרף מכוון ולא מכוון וגם גרף ממושקל. מטריצת השכנויות חייבת להיות מטריצה ריבועית.
8+
9+
עליכם לכתוב את הקבצים הבאים:
10+
11+
```
12+
Graph.cpp
13+
Algorithms.cpp
14+
```
15+
16+
הקובץ `Graph.cpp` מכיל מחלקה המייצגת גרף.
17+
המחלקה מכילה את הפעולות `loadGraph` המקבלת מטריצת שכנויות וטוענת אותה לתוך הגרף ו-`printGraph` שמדפיסה את הייצוג של הגרף (הפורמט לבחירתכם, ראו דוגמה ב-`Demo.cpp`).
18+
19+
הקובץ `Algorithms.cpp` מכיל מימושים לאלגוריתמים על גרפים. ביניהם:
20+
21+
- `isConnected(g)` - האלגוריתם מקבל גרף ומחזיר 1 אם הגרף קשיר (אחרת מחזיר 0).
22+
- `shortestPath(g,start,end)` - האלגוריתם מקבל גרף, קודקוד התחלה וקודקוד סיום ומחזיר את המסלול הקל ביותר (במקרה שהגרף לא ממושקל - הקצר ביותר) בין שני הקודקודים. במידה ואין מסלול כזה, האלגוריתם יחזיר -1.
23+
- `isContainsCycle(g)` - האלגוריתם מקבל גרף ומדפיס מעגל כלשהו. אם לא קיים מעגל בגרף, האלגוריתם יחזיר 0.
24+
- `isBipartite(g)` - האלגוריתם מקבל גרף ומחזיר את החלוקה של הגרף לגרף דו-צדדי. אם אי אפשר לחלק את הגרף, האלגוריתם יחזיר 0.
25+
- `negativeCycle(g)` - האלגוריתם מקבל גרף ומוצא מעגל שלילי (כלומר מעגל שסכום המשקלים של הצלעות שלילי). אם לא קיים מעגל כזה, האלגוריתם ידפיס שלא קיים מעגל שלילי.
26+
27+
הקובץ `Demo.cpp` מכיל דוגמאות של קלטים ופלטים.
28+
עליכם לכתוב בתחילת כל קובץ את מספר תעודת הזהות שלכם ואת המייל. כמו כן, בנוסף לקבצים של המטלה אתם נדרשים להגיש גם קובץ README המתאר את אופן המימוש ואת החלוקה שביצעתם בקוד (סוג של מדריך משתמש). אי עמידה בהנחיות תגרור הפחתה בציון. בהצלחה!
29+

Algorithms.cpp

+241
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
/*
2+
* ID: 53036281
3+
4+
* @author Samuel Lazareanu
5+
*/
6+
7+
#include <stack>
8+
#include "Algorithms.hpp"
9+
#include <iostream>
10+
#include <string>
11+
#include <queue>
12+
#include <limits>
13+
14+
using namespace std;
15+
namespace ariel{
16+
bool Algorithms::isConnected(Graph graph) {
17+
if (graph.getSize() <= 1) {
18+
return true; // an empty/1 vertice graph is connected
19+
}
20+
vector<bool> visited(graph.getSize(), false); // For each vertex, stores if it was visited yet by DFS or not. All initialized to false
21+
stack<size_t> stack;
22+
stack.push(0);
23+
24+
// Run DFS
25+
while (!stack.empty()) {
26+
// Start from the top vertex
27+
size_t vertice = stack.top();
28+
stack.pop();
29+
30+
// Mark the vertex as visited
31+
visited[vertice] = true;
32+
33+
// Add unvisited neighbors to the stack
34+
for (size_t neighbor = 0; neighbor < graph.getSize(); ++neighbor) {
35+
if (graph.getWeight(vertice, neighbor) != 0 && !visited[neighbor]) { // Check if there's an edge and if the neighbor was not visited yet
36+
stack.push(neighbor);
37+
}
38+
}
39+
}
40+
41+
// Check if all vertices are visited
42+
for (size_t ind = 0; ind < visited.size(); ++ind){
43+
if (!visited[ind]) {
44+
return false;
45+
}
46+
}
47+
48+
return true;
49+
}
50+
51+
52+
string Algorithms::shortestPath(Graph graph, size_t start, size_t end){
53+
// Run Bellman-Ford
54+
vector<size_t> path = bellmanFord(graph, start);
55+
56+
// If the path to the end vertex is MIN/MAX INT then there is no path to it (either a negative cycles or no path to it)
57+
if (graph.getSize() <= 1 || path.empty() || (path[end] == INT32_MIN || path[end] == INT32_MAX || path[end] == 0)) {
58+
return "-1";
59+
}
60+
string ans = to_string(end); // Initialize string with end vertex
61+
size_t ind = end; // Track the path back from the end
62+
while (path[ind] != start) { // Keep going until the start is reached
63+
ind = path[ind];
64+
ans.insert(0, to_string(ind) + "->");
65+
}
66+
ans.insert(0, to_string(start) + "->"); // Add the start vertex to the beginning
67+
68+
return ans;
69+
}
70+
71+
string Algorithms::isBipartite(Graph graph) {
72+
vector<string> color(graph.getSize(), "WHITE");
73+
string result; // string to store the result
74+
75+
// vectors to store vertices in setA and setB
76+
vector<size_t> setA; // BLUE
77+
vector<size_t> setB; // RED
78+
79+
// sort of BFS on the graph
80+
for (size_t ind = 0; ind < graph.getSize(); ++ind) {
81+
if (color[ind] == "WHITE") {
82+
queue<size_t> queue;
83+
color[ind] = "BLUE";
84+
setA.push_back(ind); // Add the starting vertex to setA
85+
queue.push(ind); // push starting vertex to queue
86+
87+
while (!queue.empty()) {
88+
size_t vertice = queue.front();
89+
queue.pop();
90+
91+
for (size_t neighbor = 0; neighbor < graph.getSize(); ++neighbor) {
92+
if (graph.getWeight(vertice, neighbor) != 0) {
93+
if (color[neighbor] == "WHITE") {
94+
color[neighbor] = (color[vertice] == "BLUE") ? "RED" : "BLUE";
95+
queue.push(neighbor);
96+
97+
// Add the vertex to its set based on its color
98+
if (color[neighbor] == "RED") {
99+
setB.push_back(neighbor);
100+
} else {
101+
setA.push_back(neighbor);
102+
}
103+
} else if (color[neighbor] == color[vertice]) {
104+
// If adjacent vertices have the same color, the graph is not bipartite
105+
return "0";
106+
}
107+
}
108+
}
109+
}
110+
}
111+
}
112+
113+
// Build the result string
114+
result += "The graph is bipartite: A={";
115+
for (size_t ind = 0; ind < setA.size(); ++ind) {
116+
result += to_string(setA[ind]);
117+
if (ind < setA.size() - 1) {
118+
result += ", ";
119+
}
120+
}
121+
result += "}, B={";
122+
for (size_t ind = 0; ind < setB.size(); ++ind) {
123+
result += to_string(setB[ind]);
124+
if (ind < setB.size() - 1) {
125+
result += ", ";
126+
}
127+
}
128+
result += "}";
129+
130+
return result; // Return the built result string
131+
}
132+
133+
string Algorithms::negativeCycle(Graph graph){
134+
bellmanFord(graph, 0);
135+
if(graph.getNegativeCycles()){
136+
return "Graph does have negativeCycles";
137+
}
138+
return "Graph doesn't have negativeCycles";
139+
}
140+
141+
vector<size_t> Algorithms::bellmanFord(Graph& graph, size_t source) {
142+
// Initialize distance and path vectors to MAX - each index represents a vertex
143+
vector<int> distance(graph.getSize(), INT32_MAX);
144+
vector<size_t> path(graph.getSize(), INT32_MAX);
145+
146+
distance[source] = 0; // Set distance to source vertex as 0
147+
148+
// Relax edges repeatedly (NumOfVertices - 1 times)
149+
for (int ind = 0; ind < graph.getSize()-1; ++ind){
150+
if (relaxEdges(graph, distance, path) == 0){
151+
return path; // No relaxation was made - no need to continue relaxing
152+
}
153+
}
154+
155+
// Check for negative cycles, after running relax on all edges for NumOfVertices-1 times total
156+
if (relaxEdges(graph, distance, path) > 0){
157+
// at least one edge was relaxed again - after we already relaxed all edges V-1 times -> negative edge found
158+
graph.setNegativeCycles(true); // update graph object
159+
}
160+
161+
if (graph.getNegativeCycles() || isContainsCycle(graph) == "1"){
162+
return vector<size_t>(); // A cycles exists in the graph - return an empty vector indicating no path was found
163+
}
164+
165+
return path;
166+
}
167+
168+
169+
int Algorithms::relaxEdges(Graph graph, vector<int>& distance, vector<size_t>& path){
170+
int countChanges = 0;
171+
for (size_t vertice = 0; vertice < graph.getSize(); ++vertice) {
172+
for (size_t neighbor = 0; neighbor < graph.getSize(); ++neighbor) {
173+
int weight = graph.getWeight(vertice, neighbor); // Get the weight of the edge (vertice, neighbor)
174+
// If an edge exists between the vertices && we already reached vertice u && this route is better - update it
175+
if (weight != 0 && distance[vertice] != INT32_MAX && distance[vertice] + weight < distance[neighbor]) {
176+
distance[neighbor] = distance[vertice] + weight;
177+
path[neighbor] = vertice; // Update the predecessor of neighbor to print the shortest path later
178+
countChanges++;
179+
}
180+
}
181+
}
182+
return countChanges;
183+
}
184+
185+
bool Algorithms::dfsVisit(Graph graph, size_t vertice, vector<bool>& visited, vector<size_t>& parent, vector<size_t>& cycle) {
186+
visited[vertice] = true; // Mark the curr vertex as visited
187+
188+
// Traverse all adjacent vertices of curr vertex
189+
for (size_t neighbor = 0; neighbor < graph.getSize(); ++neighbor) {
190+
int weight = graph.getWeight(vertice, neighbor); // Get the weight of the edge (vertice, neighbor)
191+
192+
// If an edge exists between the vertices and the vertex isnt the parent of curr vertex
193+
if (weight != 0 && parent[vertice] != neighbor) {
194+
// If the adjacent vertex is already visited - we found a cycle
195+
if (visited[neighbor]) {
196+
// get cycle by back tracking using parent
197+
size_t cur = vertice;
198+
while (cur != neighbor) {
199+
cycle.push_back(cur);
200+
cur = parent[cur];
201+
}
202+
cycle.push_back(neighbor);
203+
cycle.push_back(vertice);
204+
return true;
205+
}
206+
// If the vertex is not visited, recursively visit it
207+
else {
208+
parent[neighbor] = vertice; // Set the parent of the vertex
209+
if (dfsVisit(graph, neighbor, visited, parent, cycle)) {
210+
return true;
211+
}
212+
}
213+
}
214+
}
215+
return false; // No cycle found
216+
}
217+
218+
string Algorithms::isContainsCycle(Graph graph) {
219+
vector<bool> visited(graph.getSize(), false);
220+
vector<size_t> parent(graph.getSize(), INT32_MAX);
221+
vector<size_t> cycle;
222+
223+
// Iterate over all vertices and run dfsVisit if not visited yet
224+
for (size_t ind = 0; ind < graph.getSize(); ++ind) {
225+
if (!visited[ind]) {
226+
if (dfsVisit(graph, ind, visited, parent, cycle)) {
227+
string ans = "The cycle is: ";
228+
size_t indCycle = 0;
229+
for (;indCycle < cycle.size()-1; ++indCycle) {
230+
ans += to_string(cycle[indCycle]) + "->";
231+
}
232+
ans += to_string(cycle[indCycle]);
233+
graph.setCycles(true);
234+
return ans;
235+
}
236+
}
237+
}
238+
239+
return "0";
240+
}
241+
}

Algorithms.hpp

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* ID: 53036281
3+
4+
* @author Samuel Lazareanu
5+
*/
6+
7+
#pragma once
8+
#include "Graph.hpp"
9+
#include <string>
10+
11+
using namespace std;
12+
namespace ariel{
13+
class Algorithms{
14+
public:
15+
/*
16+
* Gets a graph,
17+
* returns 1 if the graph is connected (otherwise returns 0).
18+
*/
19+
static bool isConnected(Graph);
20+
21+
/*
22+
* The function receives a graph, a start vertex and an end vertex.
23+
* It returns the most light-weighted path (in case the graph is not weighted - the shortest) between the two vertices.
24+
* If there is no such route, the function will return -1.
25+
*/
26+
static string shortestPath(Graph, size_t, size_t);
27+
28+
29+
/*
30+
* Receives a graph and tried to print a cycle in it (if it succeeds, it will return 1).
31+
* If there are no circles in the graph, will return 0.
32+
*/
33+
static string isContainsCycle(Graph);
34+
35+
/*
36+
* Gets a graph and returns the partition of the graph into a bipartite graph (if it succeeds, it will return 1).
37+
* If the graph cannot be partitioned, will return 0.
38+
*
39+
* Using sort of BFS, splitting vertices into 2 sets by "coloring" them.
40+
* Adjacent vertices with the same color = the graph is not bipartite -> returns "0".
41+
* Otherwise, the graph is bipartite -> return a string with the 2 sets.
42+
*/
43+
static string isBipartite(Graph);
44+
45+
/*
46+
* Gets a graph and tries to find a negative cycle (that is, a cycle whose sum of the weights of the sides is negative).
47+
* If such circle exists, will print it, and return 1. otherwise, there are no negative circles, return 0.
48+
*/
49+
static string negativeCycle(Graph);
50+
51+
private:
52+
/*
53+
* Gets a graph and a start vertice, and runs the Bellman Ford Algorithm on it.
54+
* Updates the Graph object if negative/positive cycles are found.
55+
* Returns a path vector, allowing us to track extract the shortest path from it.
56+
*/
57+
static vector<size_t> bellmanFord(Graph&, size_t);
58+
59+
/*
60+
* Gets a graph, distances vector and paths vector (pointers).
61+
* Tries to realx all the edges one time and update the distance and path vectors.
62+
* Returns the number of relaxations performed.
63+
*/
64+
static int relaxEdges(Graph, vector<int>&, vector<size_t>&);
65+
66+
static bool dfsVisit(Graph, size_t, vector<bool>&, vector<size_t>&, vector<size_t>&);
67+
};
68+
}

0 commit comments

Comments
 (0)