|
| 1 | +#include "ActorGraph.hpp" |
| 2 | + |
| 3 | +#include <fstream> |
| 4 | +#include <iostream> |
| 5 | +#include <queue> |
| 6 | +#include <sstream> |
| 7 | +#include <stack> |
| 8 | +#include <string> |
| 9 | + |
| 10 | +#include "Node.hpp" |
| 11 | + |
| 12 | +using namespace std; |
| 13 | + |
| 14 | +// Initializing an empty graph |
| 15 | +ActorGraph::ActorGraph() { this->graph; } |
| 16 | + |
| 17 | +/* Build the actor graph from dataset file. |
| 18 | + * Each line of the dataset file must be formatted as: |
| 19 | + * ActorName <tab> MovieName <tab> Year |
| 20 | + * Two actors are connected by an undirected edge if they have worked in a movie |
| 21 | + * before. |
| 22 | + */ |
| 23 | +bool ActorGraph::buildGraph(istream& is) { |
| 24 | + bool readHeader = false; |
| 25 | + unordered_map<string, vector<Node*>> movies; |
| 26 | + |
| 27 | + while (is) { |
| 28 | + string s; |
| 29 | + if (!getline(is, s)) break; |
| 30 | + // skip the header of the file |
| 31 | + if (!readHeader) { |
| 32 | + readHeader = true; |
| 33 | + continue; |
| 34 | + } |
| 35 | + // read each line of the dataset to get the movie actor relation |
| 36 | + istringstream ss(s); |
| 37 | + vector<string> record; |
| 38 | + while (ss) { |
| 39 | + string str; |
| 40 | + if (!getline(ss, str, '\t')) break; |
| 41 | + record.push_back(str); |
| 42 | + } |
| 43 | + // if format is wrong, skip current line |
| 44 | + if (record.size() != 3) { |
| 45 | + continue; |
| 46 | + } |
| 47 | + // extract the information |
| 48 | + string actor(record[0]); |
| 49 | + string title(record[1]); |
| 50 | + int year = stoi(record[2]); |
| 51 | + // Append title with year |
| 52 | + string completeTitle = title + "#@" + record[2]; |
| 53 | + // Helper u_map |
| 54 | + if (movies.find(completeTitle) == movies.end()) { |
| 55 | + Node* curr; |
| 56 | + if (this->graph.find(actor) == this->graph.end()) { |
| 57 | + curr = new Node(actor); |
| 58 | + } else { |
| 59 | + curr = this->graph.find(actor)->second; |
| 60 | + } |
| 61 | + |
| 62 | + vector<Node*> list; |
| 63 | + list.push_back(curr); |
| 64 | + unordered_map<string, Node*> map; |
| 65 | + movies.insert({completeTitle, list}); |
| 66 | + curr->outgoing.insert({completeTitle, map}); |
| 67 | + // Found the movie title and year |
| 68 | + } else { |
| 69 | + vector<Node*> list = movies[completeTitle]; |
| 70 | + // Check if actor is already in the list |
| 71 | + bool found = false; |
| 72 | + for (Node* curr : list) { |
| 73 | + if (curr->name == actor) { |
| 74 | + found = true; |
| 75 | + } |
| 76 | + } |
| 77 | + // Actor is not in the list |
| 78 | + if (!found) { |
| 79 | + Node* curr; |
| 80 | + if (this->graph.find(actor) == this->graph.end()) { |
| 81 | + curr = new Node(actor); |
| 82 | + } else { |
| 83 | + curr = this->graph.find(actor)->second; |
| 84 | + } |
| 85 | + |
| 86 | + for (Node* tmp : list) { |
| 87 | + // If movie is not in this actor's node |
| 88 | + if (curr->outgoing.find(completeTitle) == |
| 89 | + curr->outgoing.end()) { |
| 90 | + unordered_map<string, Node*> tmpMap; |
| 91 | + tmpMap.insert({tmp->name, tmp}); |
| 92 | + curr->outgoing.insert({completeTitle, tmpMap}); |
| 93 | + } else { |
| 94 | + curr->outgoing[completeTitle].insert({tmp->name, tmp}); |
| 95 | + } |
| 96 | + curr->neighbors.push_back(tmp); |
| 97 | + tmp->outgoing[completeTitle].insert({curr->name, curr}); |
| 98 | + tmp->neighbors.push_back(curr); |
| 99 | + } |
| 100 | + movies[completeTitle].push_back(curr); |
| 101 | + } |
| 102 | + } |
| 103 | + // If it does not exist |
| 104 | + if (this->graph.find(actor) == this->graph.end()) { |
| 105 | + // Check movie title |
| 106 | + if (movies.find(completeTitle) != movies.end()) { |
| 107 | + for (Node* curr : movies[completeTitle]) { |
| 108 | + if (curr->name == actor) { |
| 109 | + // insert into graph |
| 110 | + this->graph.insert({actor, curr}); |
| 111 | + } |
| 112 | + } |
| 113 | + } |
| 114 | + } |
| 115 | + } |
| 116 | + // if failed to read the file, clear the graph and return |
| 117 | + if (!is.eof()) { |
| 118 | + this->~ActorGraph(); |
| 119 | + return false; |
| 120 | + } |
| 121 | + return true; |
| 122 | +} |
| 123 | + |
| 124 | +/* TODO */ |
| 125 | +void ActorGraph::BFS(const string& fromActor, const string& toActor, |
| 126 | + string& shortestPath) { |
| 127 | + // If the actors don't exist |
| 128 | + if (this->graph.find(toActor) == this->graph.end() || |
| 129 | + this->graph.find(fromActor) == this->graph.end()) { |
| 130 | + shortestPath = ""; |
| 131 | + return; |
| 132 | + } |
| 133 | + // reset distance and visited |
| 134 | + for (pair<string, Node*> it : graph) { |
| 135 | + it.second->distance = 0; |
| 136 | + it.second->visited = false; |
| 137 | + } |
| 138 | + // initialize empty queue |
| 139 | + queue<pair<int, Node*>> path; |
| 140 | + // push the current node with zero distance |
| 141 | + pair<int, Node*> root(0, this->graph[fromActor]); |
| 142 | + path.push(root); |
| 143 | + while (!path.empty()) { |
| 144 | + int distance = path.front().first; |
| 145 | + Node* curr = path.front().second; |
| 146 | + path.pop(); |
| 147 | + if (!curr->visited) { |
| 148 | + curr->visited = true; |
| 149 | + curr->distance = distance; |
| 150 | + // Loopping through neighbors |
| 151 | + for (auto nodes : curr->neighbors) { |
| 152 | + if (!nodes->visited) { |
| 153 | + path.push({distance + 1, nodes}); |
| 154 | + } |
| 155 | + } |
| 156 | + } |
| 157 | + } |
| 158 | + // Finding the shortest path |
| 159 | + stack<string> shortest; |
| 160 | + Node* curr = this->graph[toActor]; |
| 161 | + int isBroken = false; |
| 162 | + // if not the first actor yet |
| 163 | + while (curr->name != fromActor) { |
| 164 | + if (curr->distance == 0 && curr->name != fromActor) { |
| 165 | + shortestPath = ""; |
| 166 | + return; |
| 167 | + } |
| 168 | + // loop through movies |
| 169 | + for (auto movies : curr->outgoing) { |
| 170 | + // loop through coworkers |
| 171 | + for (auto cw : movies.second) { |
| 172 | + if (cw.second->distance == curr->distance - 1) { |
| 173 | + // push to stack |
| 174 | + shortest.push(movies.first); |
| 175 | + shortest.push(cw.first); |
| 176 | + curr = cw.second; |
| 177 | + |
| 178 | + // break |
| 179 | + isBroken = true; |
| 180 | + break; |
| 181 | + } |
| 182 | + } |
| 183 | + |
| 184 | + if (isBroken) { |
| 185 | + isBroken = false; |
| 186 | + break; |
| 187 | + } |
| 188 | + } |
| 189 | + } |
| 190 | + // Building the shortest path |
| 191 | + while (!shortest.empty()) { |
| 192 | + string tmp = "(" + shortest.top() + ")"; |
| 193 | + shortest.pop(); |
| 194 | + tmp += "--[" + shortest.top() + "]-->"; |
| 195 | + shortest.pop(); |
| 196 | + shortestPath += tmp; |
| 197 | + } |
| 198 | + shortestPath += "(" + toActor + ")"; |
| 199 | +} |
| 200 | + |
| 201 | +// Destroy the list |
| 202 | +ActorGraph::~ActorGraph() { |
| 203 | + for (auto it = this->graph.begin(); it != this->graph.end(); it++) { |
| 204 | + delete (it->second); |
| 205 | + } |
| 206 | +} |
0 commit comments