Initial commit
This commit is contained in:
189
P4/Graph.cpp
Normal file
189
P4/Graph.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
#include "Graph.h"
|
||||
#include <limits.h>
|
||||
#include "PriorityQueue.h"
|
||||
#include "illegal_exception.h"
|
||||
|
||||
/// @brief Create a new graph
|
||||
/// @param maxCount The maximum number of elements the graph is required to store
|
||||
/// @param mst A flag to enable MST (enabling makes the runtime of the graph worse)
|
||||
Graph::Graph(int maxCount, bool mst)
|
||||
{
|
||||
vertexList.reserve(maxCount);
|
||||
if (!mst)
|
||||
pq = new PriorityQueue(maxCount);
|
||||
this->maxCount = maxCount;
|
||||
this->mstEnabled = mst;
|
||||
}
|
||||
|
||||
Graph::~Graph()
|
||||
{
|
||||
delete pq;
|
||||
}
|
||||
|
||||
/// @brief Insert an edge from vertex a to b, with weight w. When MST flag is true, it is invalid to pass in edges that already exist in the graph.
|
||||
/// @param a Vertex a
|
||||
/// @param b Vertex b
|
||||
/// @param weight The weight of the edge to insert
|
||||
/// @return True if successful, false otherwise
|
||||
/// @throws illegal_exception if given invalid input
|
||||
bool Graph::Insert(int a, int b, int weight)
|
||||
{
|
||||
// Check input validity
|
||||
if (a > 50000 || a < 1 || b > 50000 || b < 1 || weight < 1)
|
||||
{
|
||||
throw illegal_exception();
|
||||
}
|
||||
|
||||
// Check if the node already exists. Skip this check when the MST is disabled - allows efficient inserts for building tree.
|
||||
int count = adjacencyList[a].size();
|
||||
if (!mstEnabled)
|
||||
{
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int currentData = std::get<0>(adjacencyList[a][i]);
|
||||
if (currentData == b)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the b vertex to the adjacency list for a
|
||||
adjacencyList[a].push_back(std::make_tuple(b, weight));
|
||||
|
||||
// Add the vertex a to the vertexList, if a has never been added to the graph
|
||||
if (count == 0)
|
||||
vertexList.insert(vertexList.begin() + vertexList.size(), std::make_tuple(a, INT_MAX, -1));
|
||||
|
||||
// Add a vertex to the adjacency list for b, and insert b into vertexList if required
|
||||
count = adjacencyList[b].size();
|
||||
adjacencyList[b].push_back(std::make_tuple(a, weight));
|
||||
if (count == 0)
|
||||
vertexList.insert(vertexList.begin() + vertexList.size(), std::make_tuple(b, INT_MAX, -1));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Delete vertex a from the graph.
|
||||
/// @param a The vertex to delete from the graph.
|
||||
/// @return True if successful, false otherwise.
|
||||
/// @throws illegal_exception if given invalid input.
|
||||
bool Graph::Delete(int a)
|
||||
{
|
||||
// Check input conditions
|
||||
if (a > 50000 || a < 1)
|
||||
{
|
||||
throw illegal_exception();
|
||||
}
|
||||
|
||||
// Ensure the vertex is in the graph
|
||||
int count = adjacencyList[a].size();
|
||||
if (count == 0)
|
||||
return false;
|
||||
|
||||
// Remove all references to the vertex in the adjacency lists of other vertices, and in the vertexList.
|
||||
while (count != 0)
|
||||
{
|
||||
std::tuple<int, int> lastData = adjacencyList[a][count - 1];
|
||||
adjacencyList[a].pop_back();
|
||||
|
||||
// Remove vertex from all adjacency lists
|
||||
for (int j = 0; j < adjacencyList[std::get<0>(lastData)].size(); j++)
|
||||
{
|
||||
if (std::get<0>(adjacencyList[std::get<0>(lastData)][j]) == a)
|
||||
{
|
||||
adjacencyList[std::get<0>(lastData)].erase(adjacencyList[std::get<0>(lastData)].begin() + j);
|
||||
}
|
||||
}
|
||||
|
||||
// If removing vertex a causes any adjacency lists to become empty, remove that vertex from the vertexList.
|
||||
if (adjacencyList[std::get<0>(lastData)].size() < 1)
|
||||
{
|
||||
for (int j = 0; j < vertexList.size(); j++)
|
||||
{
|
||||
if (std::get<0>(vertexList[j]) == std::get<0>(lastData))
|
||||
vertexList.erase(vertexList.begin() + j);
|
||||
}
|
||||
}
|
||||
|
||||
count--;
|
||||
}
|
||||
|
||||
// Remove a from the vertex list.
|
||||
for (int i = 0; i < vertexList.size(); i++)
|
||||
{
|
||||
if (std::get<0>(vertexList[i]) == a)
|
||||
vertexList.erase(vertexList.begin() + i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Get a vector containing the adjacent vertices to a
|
||||
/// @param a The vertex to check
|
||||
/// @return A vector of all vertices
|
||||
/// @throws illegal_exception if given invalid input.
|
||||
std::vector<std::tuple<int, int>> *Graph::GetAdjacent(int a)
|
||||
{
|
||||
if (a > 50000 || a < 1)
|
||||
{
|
||||
throw illegal_exception();
|
||||
}
|
||||
|
||||
return &adjacencyList[a];
|
||||
}
|
||||
|
||||
/// @brief Compute the MST of the graph
|
||||
/// @param out A vector to output the resulting MST edges in
|
||||
/// @return The cost of the MST
|
||||
int Graph::MST(std::vector<std::tuple<int, int, int>> *out)
|
||||
{
|
||||
// If there are no nodes, or somehow only one node.
|
||||
if (vertexList.size() < 2)
|
||||
return 0;
|
||||
|
||||
// When there is only one edge, that edge is the MST
|
||||
if (vertexList.size() < 3)
|
||||
{
|
||||
out->push_back(std::make_tuple(std::get<0>(vertexList[0]), std::get<0>(vertexList[1]), std::get<1>(adjacencyList[std::get<0>(vertexList[0])][0])));
|
||||
return std::get<1>(adjacencyList[std::get<0>(vertexList[0])][0]);
|
||||
}
|
||||
|
||||
// Attribution: Some of the Pseudocode used to write this function is from the CLRS textbook.
|
||||
int cost = 0;
|
||||
pq->Init(&vertexList);
|
||||
|
||||
// Apply Prim's Algorithm using a PriorityQueue.
|
||||
// Extract elements from the PQ until it is empty
|
||||
while (!pq->IsEmpty())
|
||||
{
|
||||
// Extract the element with the lowest weight
|
||||
std::tuple<int, int, int> u = pq->HeapExtractMin();
|
||||
|
||||
// Prevent insertion of first node (or any infinite weights).
|
||||
if (std::get<1>(u) != INT_MAX)
|
||||
{
|
||||
out->push_back(std::make_tuple(std::get<2>(u), std::get<0>(u), std::get<1>(u)));
|
||||
cost += std::get<1>(u);
|
||||
}
|
||||
|
||||
// Iterate through all of the adjacent vertices
|
||||
for (int i = 0; i < adjacencyList[std::get<0>(u)].size(); i++)
|
||||
{
|
||||
std::tuple<int, int> v = adjacencyList[std::get<0>(u)][i]; // The vertex being examined
|
||||
|
||||
// If the current path is better than the stored path, update it.
|
||||
if (pq->Contains(std::get<0>(v)) && std::get<1>(v) < pq->GetKey(std::get<0>(v)))
|
||||
{
|
||||
pq->Modify(std::get<0>(v), std::get<1>(v), std::get<0>(u));
|
||||
}
|
||||
}
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
/// @brief Get the number of vertices in the graph
|
||||
/// @return The number of vertices in the graph
|
||||
int Graph::GetVertexCount()
|
||||
{
|
||||
return vertexList.size();
|
||||
}
|
||||
27
P4/Graph.h
Normal file
27
P4/Graph.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef TRIE_H
|
||||
#define TRIE_H
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include "PriorityQueue.h"
|
||||
|
||||
class Graph
|
||||
{
|
||||
public:
|
||||
Graph(int maxCount, bool mst);
|
||||
~Graph();
|
||||
bool Insert(int a, int b, int weight);
|
||||
bool Delete(int a);
|
||||
std::vector<std::tuple<int, int>> *GetAdjacent(int a);
|
||||
int GetVertexCount();
|
||||
int MST(std::vector<std::tuple<int, int, int>> *outList);
|
||||
|
||||
private:
|
||||
PriorityQueue *pq;
|
||||
std::vector<std::tuple<int, int>> adjacencyList[50001]; // An array of vectors of tuples <node2, weight>
|
||||
std::vector<std::tuple<int, int, int>> vertexList; // List of all vertices in the graph stored as <node1, inf, -1>
|
||||
int maxCount;
|
||||
bool mstEnabled;
|
||||
};
|
||||
#endif
|
||||
2
P4/Makefile
Normal file
2
P4/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
all: Test.cpp Graph.cpp PriorityQueue.cpp
|
||||
g++ -std=c++11 Test.cpp Graph.cpp PriorityQueue.cpp
|
||||
153
P4/PriorityQueue.cpp
Normal file
153
P4/PriorityQueue.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "PriorityQueue.h"
|
||||
#include <limits>
|
||||
|
||||
// Attribution: Some of the Pseudocode used to write these functions is from the CLRS textbook.
|
||||
|
||||
PriorityQueue::PriorityQueue(int maxCount)
|
||||
{
|
||||
count = 0;
|
||||
locations.reserve(maxCount);
|
||||
arr.reserve(maxCount);
|
||||
for (int i = 0; i < maxCount; i++)
|
||||
{
|
||||
locations[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Initialize the priority queue with the given vector of tuples. The tuples should be in the format: <vertex, weight, parent>
|
||||
/// @param vertexList The vector of tuples to insert into the priority queue.
|
||||
void PriorityQueue::Init(std::vector<std::tuple<int, int, int>> *vertexList)
|
||||
{
|
||||
count = vertexList->size();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
arr[i] = vertexList->at(i);
|
||||
locations[std::get<0>(vertexList->at(i))] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Modify the weight and/or the parent of vertex v
|
||||
/// @param v The vertex to modify (not the index in the priority queue)
|
||||
/// @param w The weight to set
|
||||
/// @param parent The parent to set
|
||||
/// @return True if successful, false otherwise
|
||||
bool PriorityQueue::Modify(int v, int w, int parent)
|
||||
{
|
||||
if (count < 1)
|
||||
return false;
|
||||
|
||||
int i = locations[v];
|
||||
if (i < 0)
|
||||
return false;
|
||||
|
||||
std::get<1>(arr[i]) = w;
|
||||
std::get<2>(arr[i]) = parent;
|
||||
while (i > 0 && std::get<1>(arr[Parent(i)]) > std::get<1>(arr[i]))
|
||||
{
|
||||
|
||||
Exchange(i, Parent(i));
|
||||
i = Parent(i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Extract the smallest value from the priority queue (the topmost element, since this is a min pq).
|
||||
/// @return The element in the priority queue if not empty, or a tuple of all ints <-1, -1, -1> if empty
|
||||
std::tuple<int, int, int> PriorityQueue::HeapExtractMin()
|
||||
{
|
||||
// Signal no minimum element if there are no elements.
|
||||
if (IsEmpty())
|
||||
return std::make_tuple<int, int, int>(-1, -1, -1);
|
||||
|
||||
std::tuple<int, int, int> min = arr[0];
|
||||
|
||||
locations[std::get<0>(arr[count - 1])] = 0;
|
||||
locations[std::get<0>(min)] = -1;
|
||||
arr[0] = arr[count - 1];
|
||||
count--;
|
||||
MinHeapify(0);
|
||||
return min;
|
||||
}
|
||||
|
||||
/// @brief Run the MinHeapify algorithm on the pq.
|
||||
/// @param i The index (not the vertex).
|
||||
void PriorityQueue::MinHeapify(int i)
|
||||
{
|
||||
int l = Left(i);
|
||||
int r = Right(i);
|
||||
|
||||
int smallest = i;
|
||||
|
||||
if (l < count && std::get<1>(arr[l]) < std::get<1>(arr[i]))
|
||||
smallest = l;
|
||||
|
||||
if (r < count && std::get<1>(arr[r]) < std::get<1>(arr[smallest]))
|
||||
smallest = r;
|
||||
|
||||
if (smallest != i)
|
||||
{
|
||||
Exchange(i, smallest);
|
||||
MinHeapify(smallest);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Get a boolean value to determine if the priority queue is empty
|
||||
/// @return True if the priority queue is empty, false otherwise.
|
||||
bool PriorityQueue::IsEmpty()
|
||||
{
|
||||
return count < 1;
|
||||
}
|
||||
|
||||
/// @brief Check if the priority queue contains a vertex v.
|
||||
/// @param v The vertex (not index) to check.
|
||||
/// @return True if the vertex (not index) is contained in the priority queue, false otherwise.
|
||||
bool PriorityQueue::Contains(int v)
|
||||
{
|
||||
return locations[v] != -1;
|
||||
}
|
||||
|
||||
/// @brief Get the key (weight) of a vertex (not index) in the priority queue
|
||||
/// @param v The vertex (not index) to get the weight for
|
||||
/// @return The key (weight) of the vertex
|
||||
int PriorityQueue::GetKey(int v)
|
||||
{
|
||||
return std::get<1>(arr[locations[v]]);
|
||||
}
|
||||
|
||||
/// @brief Swap the elements of index a and index b (not the vertex number)
|
||||
/// @param a The index (not vertex) of the element to swap
|
||||
/// @param b The index (not vertex) of the element to swap
|
||||
void PriorityQueue::Exchange(int a, int b)
|
||||
{
|
||||
locations[std::get<0>(arr[a])] = b;
|
||||
locations[std::get<0>(arr[b])] = a;
|
||||
|
||||
std::tuple<int, int, int> temp = arr[a];
|
||||
arr[a] = arr[b];
|
||||
arr[b] = temp;
|
||||
}
|
||||
|
||||
/// @brief Given an index (not vertex), get the parent of that index (not vertex)
|
||||
/// @param i The index (not vertex) to get the parent of
|
||||
/// @return The index (not vertex) of the parent
|
||||
int PriorityQueue::Parent(int i)
|
||||
{
|
||||
return (i - 1) / 2;
|
||||
}
|
||||
|
||||
/// @brief Given an index (not vertex), get the left child of that index (not vertex)
|
||||
/// @param i The index (not vertex) to get the left child of
|
||||
/// @return The index (not vertex) of the left child
|
||||
int PriorityQueue::Left(int i)
|
||||
{
|
||||
return 2 * i + 1;
|
||||
}
|
||||
|
||||
/// @brief Given an index (not vertex), get the right child of that index (not vertex)
|
||||
/// @param i The index (not vertex) to get the right child of
|
||||
/// @return The index (not vertex) of the right child
|
||||
int PriorityQueue::Right(int i)
|
||||
{
|
||||
return 2 * i + 2;
|
||||
}
|
||||
31
P4/PriorityQueue.h
Normal file
31
P4/PriorityQueue.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef PRIORITYQUEUE_H
|
||||
#define PRIORITYQUEUE_H
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
class PriorityQueue
|
||||
{
|
||||
public:
|
||||
PriorityQueue(int maxCount);
|
||||
void Init(std::vector<std::tuple<int, int, int>> *vertexList);
|
||||
std::tuple<int, int, int> HeapExtractMin();
|
||||
bool Modify(int i, int w, int parent);
|
||||
bool Contains(int v);
|
||||
bool IsEmpty();
|
||||
int GetKey(int v);
|
||||
|
||||
private:
|
||||
int count;
|
||||
void Exchange(int a, int b);
|
||||
void MinHeapify(int i);
|
||||
int Parent(int i);
|
||||
int Left(int i);
|
||||
int Right(int i);
|
||||
std::vector<std::tuple<int, int, int>> arr; // The actual priority queue.
|
||||
std::vector<int> locations; // References for vertex -> PQ index. Allows indexing PQ in O(1) time.
|
||||
const int MAX_UNIQUE_KEYS = 50001;
|
||||
};
|
||||
|
||||
#endif
|
||||
180
P4/Test.cpp
Normal file
180
P4/Test.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "Graph.h"
|
||||
#include "illegal_exception.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
const int MAX_NODE_COUNT = 50001;
|
||||
|
||||
int main()
|
||||
{
|
||||
string cmd;
|
||||
Graph *graph = new Graph(MAX_NODE_COUNT, false);
|
||||
bool mstModified = true;
|
||||
int prevCost = 0;
|
||||
std::vector<std::tuple<int, int, int>> prevOutput;
|
||||
|
||||
while (cin >> cmd)
|
||||
{
|
||||
if (cmd == "LOAD")
|
||||
{
|
||||
mstModified = true;
|
||||
string filename;
|
||||
cin >> filename;
|
||||
|
||||
ifstream fin(filename.c_str());
|
||||
|
||||
int count;
|
||||
fin >> count;
|
||||
|
||||
int n1;
|
||||
while (fin >> n1)
|
||||
{
|
||||
int n2;
|
||||
fin >> n2;
|
||||
int w;
|
||||
fin >> w;
|
||||
|
||||
graph->Insert(n1, n2, w);
|
||||
}
|
||||
|
||||
fin.close();
|
||||
cout << "success" << endl;
|
||||
}
|
||||
else if (cmd == "INSERT")
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
int w;
|
||||
|
||||
cin >> a;
|
||||
cin >> b;
|
||||
cin >> w;
|
||||
|
||||
try
|
||||
{
|
||||
if (graph->Insert(a, b, w))
|
||||
{
|
||||
mstModified = true;
|
||||
std::cout << "success" << std::endl;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
std::cout << "failure" << std::endl;
|
||||
}
|
||||
}
|
||||
catch (illegal_exception)
|
||||
{
|
||||
std::cout << "illegal argument" << std::endl;
|
||||
}
|
||||
}
|
||||
else if (cmd == "DELETE")
|
||||
{
|
||||
int a;
|
||||
cin >> a;
|
||||
|
||||
try
|
||||
{
|
||||
if (graph->Delete(a))
|
||||
{
|
||||
mstModified = true;
|
||||
std::cout << "success" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "failure" << std::endl;
|
||||
}
|
||||
}
|
||||
catch (illegal_exception)
|
||||
{
|
||||
std::cout << "illegal argument" << std::endl;
|
||||
}
|
||||
}
|
||||
else if (cmd == "PRINT")
|
||||
{
|
||||
int a;
|
||||
cin >> a;
|
||||
|
||||
try
|
||||
{
|
||||
std::vector<std::tuple<int, int>> *out = graph->GetAdjacent(a);
|
||||
for (int i = 0; i < out->size(); i++)
|
||||
{
|
||||
std::cout << get<0>(out->at(i)) << " ";
|
||||
}
|
||||
|
||||
if (out->size() < 1)
|
||||
{
|
||||
std::cout << "failure";
|
||||
}
|
||||
std::cout << endl;
|
||||
}
|
||||
catch (illegal_exception)
|
||||
{
|
||||
std::cout << "illegal argument" << std::endl;
|
||||
}
|
||||
}
|
||||
else if (cmd == "MST")
|
||||
{
|
||||
// Prevent useless regeneration of MST.
|
||||
if (!mstModified)
|
||||
{
|
||||
if (prevOutput.size() < 1)
|
||||
std::cout << "failure";
|
||||
|
||||
for (int i = 0; i < prevOutput.size(); i++)
|
||||
{
|
||||
std::cout << std::get<0>(prevOutput[i]) << " " << std::get<1>(prevOutput[i]) << " " << std::get<2>(prevOutput[i]) << " ";
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::tuple<int, int, int>> out;
|
||||
out.reserve(graph->GetVertexCount());
|
||||
prevCost = graph->MST(&out);
|
||||
prevOutput = out;
|
||||
|
||||
for (int i = 0; i < out.size(); i++)
|
||||
{
|
||||
std::cout << std::get<0>(out[i]) << " " << std::get<1>(out[i]) << " " << std::get<2>(out[i]) << " ";
|
||||
}
|
||||
|
||||
if (out.size() < 1)
|
||||
std::cout << "failure";
|
||||
|
||||
std::cout << std::endl;
|
||||
mstModified = false;
|
||||
}
|
||||
}
|
||||
else if (cmd == "COST")
|
||||
{
|
||||
// Prevent useless regeneration of MST.
|
||||
if (!mstModified)
|
||||
{
|
||||
std::cout << "cost is " << prevCost << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::tuple<int, int, int>> out;
|
||||
out.reserve(graph->GetVertexCount());
|
||||
int cost = graph->MST(&out);
|
||||
prevOutput = out;
|
||||
prevCost = cost;
|
||||
|
||||
std::cout << "cost is " << cost << std::endl;
|
||||
mstModified = false;
|
||||
}
|
||||
}
|
||||
|
||||
else if (cmd == "END")
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete graph;
|
||||
}
|
||||
10
P4/illegal_exception.h
Normal file
10
P4/illegal_exception.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef ILLEGAL_EXCEPTION_H
|
||||
#define ILLEGAL_EXCEPTION_H
|
||||
|
||||
#include <exception>
|
||||
|
||||
class illegal_exception : std::exception
|
||||
{
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user