I am getting a TLE error while performing cycle detection - c++

I have written a code to the leetcode problem(courseSchedule) which basically asks whether a given set of courses can be done given dependencies. my approach is to create a graph and then check for a cycle, however, it's giving a TLE error. Can you help me as to why is the TLE happening or if there's a better approach that I can use ?
bool cycle( vector<vector<int>> &adj,int i,vector<bool> vis){
if(vis[i])
return true;
vis[i]=true;
for(int k=0;k<adj[i].size();k++)
if(cycle(adj,adj[i][k],vis))
return true;
return false;
}
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> adj(numCourses);
for(int i=0;i<prerequisites.size();i++)
adj[prerequisites[i][1]].push_back(prerequisites[i][0]);
vector<bool> vis(numCourses,false);
for(int i=0;i<numCourses;i++)
if(cycle(adj,i,vis))
return false;
return true;
}
};

Actually, your function is correct but so inefficient.
This is because in the cycle function performs so many redundant operations i.e check for the same node multiple times.
Your Code:
bool cycle( vector<vector<int>> &adj,int i,vector<bool> vis){
if(vis[i])
return true;
vis[i] = true;
for(int k = 0; k < adj[i].size(); k++)
if(cycle(adj, adj[i][k], vis))
return true;
return false;
}
Ex:
0 ---> 1 ---> 2 ......... (some more edges)
0 ---> 3 ---> 2 ---> 4 ........ (some more edges)
So, for this graph, for the start vertex 0 (with your code) for the bool function:
iteration - 1: you perform the DFS and check for 1 and 2 and
......
iteration - 2: you perform the DFS and check for 3 and again 2 .....
So, like this, you will be recomputing the same sub-problems. To avoid this you need to put another array just check if a node is already computed.
So I have introduced another vector var (initialized to false) which basically sets to true if node is visited and got approved as non-cycle node (which doesn't involve in a cycle) .
Improved Code:
bool cycle( vector<vector<int>> &adj,int i,vector<bool> vis, vector<bool>& var){
// if i involves in cycle and visited in the current sequence
if(!var[i] and vis[i])
return true;
vis[i] = true;
for(int k=0;k<adj[i].size();k++) {
// if adj[i][k] is true i.e doesn't involve in cycle, so no need to check it. If it is false we should check it.
if(!var[adj[i][k]] and cycle(adj,adj[i][k],vis, var))
return true;
else
var[adj[i][k]] = true; // else setting true to tell it doesn't involve in cycle
}
// setting true to tell it doesn't involve in cycle
var[i] = true;
return false;
}
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> adj(numCourses);
for(int i=0;i<prerequisites.size();i++)
adj[prerequisites[i][1]].push_back(prerequisites[i][0]);
vector<bool> vis(numCourses,false);
vector<bool> var(numCourses,false);
for(int i=0;i<numCourses;i++)
if(cycle(adj,i,vis, var))
return false;
return true;
}
};
Note:
I just made small changes to make your code overcome TLE without changing the basic logic. But this is still inefficient as your logic needs to pass the vector by value. I suggest you think another way :)

I also think vis is not passed by reference would be the problem for large size test cases.
This is a similar depth first search graph method, that'd pass through:
#include <cstdint>
#include <utility>
#include <vector>
const static struct Solution {
static bool canFinish(
const int num_courses,
const std::vector<std::vector<int>>& prerequisites
) {
GraphType graph = buildCourseGraph(prerequisites, num_courses);
std::vector<bool> to_take(num_courses, false);
std::vector<bool> taken(num_courses, false);
for (SizeType course = 0; course < num_courses; ++course) {
if (!taken[course] && !validateAcyclic(graph, course, to_take, taken)) {
return false;
}
}
return true;
}
private:
using GraphType = std::vector<std::vector<int>>;
using SizeType = std::uint_fast16_t;
static GraphType buildCourseGraph(
const std::vector<std::vector<int>>& prerequisites,
const SizeType num_courses
) {
GraphType graph(num_courses);
for (const auto& prerequisite : prerequisites) {
graph[prerequisite[1]].emplace_back(prerequisite[0]);
}
return graph;
}
static bool validateAcyclic(
const GraphType& graph,
const SizeType& course,
std::vector<bool>& to_take,
std::vector<bool>& taken
) {
if (to_take[course]) {
return false;
}
if (taken[course]) {
return true;
}
to_take[course] = taken[course] = true;
for (const auto& adj_course : graph[course]) {
if (!validateAcyclic(graph, adj_course, to_take, taken)) {
return false;
}
}
to_take[course] = false;
return true;
}
};
and here is LeetCode's depth first search solution in Java (with comments):
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
// course -> list of next courses
HashMap<Integer, List<Integer>> courseDict = new HashMap<>();
// build the graph first
for (int[] relation : prerequisites) {
// relation[0] depends on relation[1]
if (courseDict.containsKey(relation[1])) {
courseDict.get(relation[1]).add(relation[0]);
} else {
List<Integer> nextCourses = new LinkedList<>();
nextCourses.add(relation[0]);
courseDict.put(relation[1], nextCourses);
}
}
boolean[] checked = new boolean[numCourses];
boolean[] path = new boolean[numCourses];
for (int currCourse = 0; currCourse < numCourses; ++currCourse) {
if (this.isCyclic(currCourse, courseDict, checked, path))
return false;
}
return true;
}
/*
* postorder DFS check that no cycle would be formed starting from currCourse
*/
protected boolean isCyclic(
Integer currCourse, HashMap<Integer, List<Integer>> courseDict,
boolean[] checked, boolean[] path) {
// bottom cases
if (checked[currCourse])
// this node has been checked, no cycle would be formed with this node.
return false;
if (path[currCourse])
// come across a previously visited node, i.e. detect the cycle
return true;
// no following courses, no loop.
if (!courseDict.containsKey(currCourse))
return false;
// before backtracking, mark the node in the path
path[currCourse] = true;
boolean ret = false;
// postorder DFS, to visit all its children first.
for (Integer child : courseDict.get(currCourse)) {
ret = this.isCyclic(child, courseDict, checked, path);
if (ret)
break;
}
// after the visits of children, we come back to process the node itself
// remove the node from the path
path[currCourse] = false;
// Now that we've visited the nodes in the downstream,
// we complete the check of this node.
checked[currCourse] = true;
return ret;
}
}
References
For additional details, please see the Discussion Board which you can find plenty of well-explained accepted solutions in there, with a variety of languages including efficient algorithms and asymptotic time/space complexity analysis1, 2.

Related

How can I optimize Astar for vast empty spaces?

I am creating a game with a 3D grid for flying entities, So I have a lot of points and connections in the air where there aren't any obstructions. I didn't want to decrease the resolution of my grid so I thought I could just skip over chunks (or empties as I call them) of the Astar map while they're not containing any obstructions, and I modified Godot's Astar algorithm to do this.
Unfortunately this ended up being slower than looping through points one at a time due to the way I implemented this modification, which needs to loop through all the edge points of an empty.
2D representation of how one edge point of an empty connects to all other edge points:
This ends up looping through a larger number of points than letting the A* algorithm work it's way through the empty.
So I'm sorta stumped on how to make this more efficient while still preserving the most optimal path.
I could potentially narrow down what faces of the empty should be scanned over by first comparing the center points of all 8 faces of the empty (as my grid consists of hexagonal prisms). Or maybe I should somehow use the face center points of the empty's faces exclusively instead of all edge points.
I mainly want to know if anyone has worked on an issue like this before, and if so what would be the recommended solution?
Here is the astar loop for reference:
bool AStar::_solve(Point *begin_point, Point *end_point, int relevant_layers) {
pass++;
//make sure parallel layers are supported
// or if *relevant_layers is 0 then use all points
bool supported = relevant_layers == 0 || (relevant_layers & end_point->parallel_support_layers) > 0;
if (!end_point->enabled || !supported) {
return false;
}
bool found_route = false;
Vector<Point *> open_list;
SortArray<Point *, SortPoints> sorter;
begin_point->g_score = 0;
begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
open_list.push_back(begin_point);
while (!open_list.empty()) {
Point *p = open_list[0]; // The currently processed point
if (p == end_point) {
found_route = true;
break;
}
sorter.pop_heap(0, open_list.size(), open_list.ptrw()); // Remove the current point from the open list
open_list.remove(open_list.size() - 1);
p->closed_pass = pass; // Mark the point as closed
//if the point is part of an empty, look through all of the edge points of said empty (as to skip over any points within the empty).
OAHashMap<int, Point*> connections;
PoolVector<Empty*> enabled_empties;
int size = p->empties.size();
PoolVector<Empty*>::Read r = p->empties.read();
for (int i = 0; i < size; i++) {
Empty* e = r[i];
supported = relevant_layers == 0 || (relevant_layers & e->parallel_support_layers) > 0;
//if the empty is enabled and the end point is not within the empty
if (e->enabled && supported && !end_point->empties.has(e)) {
enabled_empties.append(e);
//can travel to any edge point
for (OAHashMap<int, Point*>::Iterator it = e->edge_points.iter(); it.valid; it = e->edge_points.next_iter(it)) {
int id = *it.key;
Point* ep = *(it.value);
ep->is_neighbour = false;
//don't connect to the same point
if (id != p->id && (i == 0 || !connections.has(id))) {
connections.set(id, ep);
}
}
}
}
//add neighbours to connections
for (OAHashMap<int, Point*>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
int id = *it.key;
Point* np = *(it.value);// The neighbour point
np->is_neighbour = true;
//don't need to check for duplicate point connections if no empties
if (size == 0 || !connections.has(id)) {
//don't add points within enabled empties since they're meant to be skipped over
if (np->empties.size() > 0 && !np->on_empty_edge) {
bool in_enabled_empty = false;
PoolVector<Empty*>::Read r1 = np->empties.read();
for (int i = 0; i < np->empties.size(); i++) {
if (enabled_empties.has(r1[i])) {
in_enabled_empty = true;
break;
}
}
if (!in_enabled_empty) {
connections.set(id, np);
}
}
else {
connections.set(id, np);
}
}
}
for (OAHashMap<int, Point *>::Iterator it = connections.iter(); it.valid; it = connections.next_iter(it)) {
Point *e = *(it.value); // The neighbour point
//make sure parallel layers are supported
// or if *relevant_layers is 0 then use all points
supported = relevant_layers == 0 || (relevant_layers & e->parallel_support_layers) > 0;
if (!e->enabled || e->closed_pass == pass || !supported) {
continue;
}
real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * e->weight_scale;
bool new_point = false;
if (e->open_pass != pass) { // The point wasn't inside the open list.
e->open_pass = pass;
open_list.push_back(e);
new_point = true;
} else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous.
continue;
}
e->prev_point = p;
e->prev_point_connected = e->is_neighbour;
e->g_score = tentative_g_score;
e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
if (new_point) { // The position of the new points is already known.
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptrw());
} else {
sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw());
}
}
}
return found_route;
}
Note: I'm still not exactly sure what the sorter does.
the entire code can be seen here in a_star.cpp and a_star.h
Edit:
if anyone wants to reference or use this, I've modified the Astar code to add user-defined octants and to use a user-defined straight line function (they are user-defined so they can work with any type of grid) to be used between octants when possible to further decrease runtime, and it works very well in terms of speed. Though the pathing is not optimal, especially when adding a lot of obstacles/restricting the available positions.

Fastest solution to Testdome excercise about repeating playlist

I tried to an exercise on TestDome about discovering if a playlist has repetitions ( TestDome C++ Playlist )
I tried to solve in this way:
bool isRepeatingPlaylist()
{
std::map<std::string, int> songs;
Song* pSong = this;
while (pSong != nullptr) {
if (songs[pSong->name] > 0)
return true;
songs[pSong->name]++;
pSong = pSong->nextSong;
}
return false;
}
The feedback is that I passed 3 out of 4 test cases. The test case I'm not passing is the one about performances. Can you help me improving?
The 4th test is "Performance test on a large playlist"
when it comes to efficiency and you don't need ordered data you should use unordered_set<> or unordered_map<>.
map<> search complexity is O(log n) but it's average O(1) to O(n) in the worst case for unordered_set<> or unordered_map<>.
below code passed all 4 tests of TestDome C++ Playlist
bool isRepeatingPlaylist()
{
std::unordered_set<std::string> playedSongs;
Song *pSong = this;
while (pSong != nullptr)
{
if (playedSongs.find(pSong->name) == playedSongs.end())
playedSongs.insert(pSong->name);
else
return true;
pSong = pSong->nextSong;
}
return false;
}
to use it just add #include<unordered_set>

LeetCode 380: Insert Delete GetRandom O(1)

I came across this leetcode problem Insert Delete GetRandom where it is asked to implement a Data Structure to support Insert, Delete and getRandom in average O(1) time, and solved it as using map and a vector.
My solution passes all the test cases except for the last one and I'm not able to figure out why? The last test case is really very large to debug.
I changed my code a little bit and then it passes but still didn't got why the previous one didn't pass.
Non-Accepted Solution:
class RandomizedSet {
map<int, int> mp;
vector<int> v;
public:
/** Initialize your data structure here. */
RandomizedSet() {
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
bool insert(int val) {
if(mp.find(val) == mp.end()){
v.push_back(val);
mp[val] = v.size()-1;
return true;
}
else return false;
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
bool remove(int val) {
if(mp.find(val) == mp.end()){
return false;
}
else{
int idx = mp[val];
mp.erase(val);
swap(v[idx], v[v.size()-1]);
v.pop_back();
if(mp.size()!=0) mp[v[idx]] = idx;
return true;
}
}
/** Get a random element from the set. */
int getRandom() {
if(v.size() == 0) return 0;
int rndm = rand()%v.size();
return v[rndm];
}
};
/**
* Your RandomizedSet object will be instantiated and called as such:
* RandomizedSet* obj = new RandomizedSet();
* bool param_1 = obj->insert(val);
* bool param_2 = obj->remove(val);
* int param_3 = obj->getRandom();
*/
Accpeted Solution:
The problem is in remove function, when i change the remove function by below code, it passes.
if(mp.find(val) == mp.end()){
return false;
}
else{
int idx = mp[val];
swap(v[idx], v[v.size()-1]);
v.pop_back();
mp[v[idx]] = idx;
mp.erase(val);
return true;
}
I don't understand why is this happening. I placed the mp.erase(val) in the last and replaced the if(mp.size()!=0) mp[v[idx]] = idx to mp[v[idx]] = idx only.
Both versions of remove function are able to handle corner case - when there is only single element left in the map and we want to remove it.
LeetCode 380
This is because of undefined behavior when the element removed is the last element.
e.g, say the operations are
insert(1) // v = [1], mp = [1->0]
insert(2) // v = [1,2], mp = [1->0, 2->1]
remove(2):
int idx = mp[val]; // val = 2, idx = 1
mp.erase(val); // mp = [1->0]
swap(v[idx], v[v.size()-1]); // idx = v.size()-1 = 1, so this does nothing.
v.pop_back(); // v = [1]
if(mp.size()!=0) mp[v[idx]] = idx; // mp[v[1]] = 1.
// But v[1] is undefined after pop_back(), since v's size is 1 at this point.
I am guessing that it doesn't clear the memory location accessed by v[1], so v[1] still points to 2, and it ends up putting 2 back into mp.

Iterating over vector of custom object with two conditions

Using C++11, I'd like to iterate over a vector and return a type that indicates that the index was not found.
I am use to the traditional for(;;) loop and specifying the index manually, as my code shows below.
inline std::size_t ItemList::FindItem(Items& Item)
{
for (std::size_t i = 0; i < ItemVector.size(); i++)
{
if (ItemVector[i]->GetId() == Item.GetId() && !ItemVector[i]->GetName().compare(Item.GetName()))
{
return i + 1;
}
}
return 0;
}
I'm also having to increment the index +1 in order to return a value of 0 (to accommodate unsigned size_t) to indicate the calling method that the index was not found (I understand this is asinine). I am assuming it would be more suitable to return something more like std::end()?
Would using a C++11 iterator approach be more efficient? The vector will populate to a large number and the find needs to be quick.
You could use std::find_if and work with iterators:
auto it = std::find_if(ItemVector.begin(), ItemVector.end(),
[&Item](Items *value) {
return value->GetId() == Item.GetId() && !value->GetName().compare(Item.GetName());
}
);
Then you can simply test if it != ItemVector.end() to know if you found something.
There will likely be no (or very small) difference between this and your version in term of speed, but it is a cleaner way to check if something was found or not.
Yes, an iterator would be the way to do this, you're actually writing your own version of find_if You could instead do:
find_if(cbegin(ItemVector), cend(ItemVector), [&](const auto& i){ return i.GetId() == Item.GetId() && i.GetName() != Item.GetName(); })
You can test whether the result of this function was found by testing for equality with cend(ItemVector).
Additionally if you need to find the index of the item you can pass this result after cbegin(ItemVector) to: distance
Live Example
My solution for double search condition that Lambda has multiple parameters in find_if
bool check_second_loop(FullFrame *image_track, guint64 object_id, bool *deletion)
{
auto itr= std::find_if(image_track->track_ids.begin(),
image_track->track_ids.end(),
[object_id](const guint64& a)
{
return a == object_id;
});
if (itr != image_track->track_ids.end())
{
image_track->track_ids.erase(itr);
if(image_track->track_ids.size()==0)
{
*deletion = true;
}
return true;
}
else
return false;
}
bool check_first_loop(guint64 object_id, gint source_id)
{
bool deletion = false;
auto it = find_if(full_frame_list.begin(), full_frame_list.end(),
[object_id, &deletion, source_id](FullFrame &x)
{
return check_second_loop(&x, object_id, &deletion)
&& x.camera_number == source_id;
});
if (it != full_frame_list.end())
{
// Found
return true;
}
else
return false;
}

Graph Traversal Problem

My Dijkstra Algorithm works fine to find a path. Now I want to go back to show the way I went. I mark a visited vertex and give it a pointer to the vertex I came from "prev". Unfortunately these pointers get manipulated in some way when looping in the while loop, so that the vertices at the end don't know where they came from. Can you help me?
Probably it's a pointer problem I don't get. I have a copy constructor and a =operator.
int MyMatrix::searchBreadth(MyVertex &from,MyVertex &to,int mode)
{
queue<MyVertex> q;//queue
vector<MyVertex> nb;//vector of neighbours
path=INFINITY;//path is very long
visits.push_back(from.getName());
from.setDistance(0);
MyVertex n("start");
from.setPrev(n);
q.push(from);
while(!q.empty())
{
MyVertex v=q.front();
q.pop();
int k=v.getDistance();
nb.clear();
nb = getNeighbours(v);
for(unsigned int i=0;i<nb.size();i++)
{
if((!nb[i].getPrev())&&path==INFINITY) nb[i].setPrev(v);
if(!mode){//unweighted
if(!wasVisited(nb[i].getName())){
nb[i].setDistance(k+1);
q.push(nb[i]);
}
}
if(mode){//length or weight
if(!wasVisited(nb[i].getName())){
int cost=0;
MyEdge e = m->getEdge(v,nb[i]);
if(mode==1)cost=(int) e.getLength();//length
if(mode==2)cost=(int) e.getWeight();//weigth
nb[i].setDistance(k+cost);
q.push(nb[i]);
}
}
if((nb[i].getName().compare(to.getName())==0) && (!wasVisited(nb[i].getName()))){//path found
int j=nb[i].getDistance();
if(j<path)path=j;
}
visits.push_back(nb[i].getName());
}//end of for
//end of 0size if
}//end of while
return path;
}
MyVertex::MyVertex()
{
name="null";
dist=0;
visited=false;
prev=0;
}
MyVertex::MyVertex(string name)
{
this->name=name;
visited=false;
dist=numeric_limits<int>::max();
prev=0;
}
MyVertex::~MyVertex(void)
{
if (!prev) prev=0;
}
MyVertex::MyVertex(const MyVertex& V){
this->name = V.name;
this->visited=V.visited;
this->dist=V.dist;
this->prev=V.prev;
}
MyVertex& MyVertex::operator=(const MyVertex& L){
if (this == &L){ return *this;
}else{
delete prev;
dist=L.dist;
name=L.name;
visited=L.visited;
prev=L.prev;
}
return *this;
}
You're leaving a lot of code out, but you seem to be adjusting the Distance of a node -- and setting its prev -- without first checking whether it already has a lesser Distance. And once you've found any path, you stop setting prev, so that if you later find a shorter path, its nodes may not be marked.