Reputation: 49
I have a exercise to do fiber network skeleton. Each node is represented by IP address. On input I get commands to build network (B) and then commands to check (T) if there is connection between one node and another.
For every T commands I must to answer: yes or no. So in this example it will be: Y N Y Y N. I have good results, but my solution fails with execution time - I check it here: https://pl.spoj.com/problems/TFIBRE/
My code (edited):
#include <iostream>
#include <string>
#include <unordered_map>
#include <list>
#include <set>
#include <arpa/inet.h>
using namespace std;
class Graph
{
std::unordered_map<uint32_t, bool> visited;
std::unordered_map<uint32_t, set<uint32_t>> adj;
bool DFSUtil(const uint32_t& v, const uint32_t&, std::unordered_map<uint32_t, bool>& visited);
public:
Graph(): visited{}, adj{}
{}
void addEdge(const uint32_t& v, const uint32_t& w);
bool DFS(const uint32_t& v, const uint32_t& el);
};
void Graph::addEdge(const uint32_t& v, const uint32_t& w)
{
adj[v].insert(w);
adj[w].insert(v);
}
bool Graph::DFSUtil(const uint32_t& v, const uint32_t& el, std::unordered_map<uint32_t, bool>& visited)
{
if (v == el){
return true;
}
visited[v] = true;
set<uint32_t>::iterator i;
for (i = adj[v].begin(); i != adj[v].end(); ++i)
if (!visited[*i]){
if(DFSUtil(*i, el, visited))
return true;
else
continue;
}
return false;
}
bool Graph::DFS(const uint32_t& v, const uint32_t& el)
{
visited.clear();
for (std::pair<uint32_t, set<uint32_t>> element : adj)
visited[element.first] = false;
return DFSUtil(v, el, visited);
}
int main()
{
Graph g;
char command;
char addr1[16];
char addr2[16];
in_addr ip1;
in_addr ip2;
while (scanf("%c %s %s ", &command, addr1, addr2) == 3)
{
inet_aton(addr1, &ip1);
inet_aton(addr2, &ip2);
if (command == 'B')
g.addEdge(ip1.s_addr, ip2.s_addr);
else
cout << (g.DFS(ip1.s_addr, ip2.s_addr) ? "T\n" : "N\n");
}
return 0;
}
Searching takes the longest time. What can I do to improve this? Maybe I shouldn't use stl containers? but what to use then?
Upvotes: 0
Views: 137
Reputation: 19621
If links are bi-directional and there is no limitation on distance you could try using https://en.wikipedia.org/wiki/Disjoint-set_data_structure (a.k.a. Union-Find). When you see a link, merge the sets owning the two ends of the link. To test for connectivity, see if the two ends have been merged into the same set.
Upvotes: 1
Reputation: 249502
An obvious problem is that you're allocating memory unnecessarily many times.
To start with you create a fresh std::vector<std::string> words;
for every line which is pointless because you know each line has three words. So you can do this:
char command; // B or T
char addr1[16];
char addr2[16];
while (scanf("%c %s %s", &command, addr1, addr2) == 3)
{
if (command == 'B')
g.addEdge(addr1, addr2);
else
cout << g.DFS(addr1, addr2) ? "Y\n" : "N\n";
}
Note I also removed your use of endl
which is slow because it forces a buffer flush every time.
Continue my work to remove more allocations from your code. For example your DFS() creates a new unordered_map every time, which is totally unnecessary, it could use the same map every time and just clear() it, reusing the same allocated memory. And using std::string is pointless when you know the strings are always between 8 and 16 bytes including the null terminator.
Even this line allocates memory!
for (std::pair<std::string, set<std::string>> element : adj)
It should use a reference, not a copy of each value:
for (auto& element : adj)
Upvotes: 0