A variant of Dijkstra algorithm - c++

I found this algorithm from CP3 book for ICPC, it is a variant of Dijkstra but it gives TLE in some cases (hidden tests). Although it seems that the running time of this algorithm is same as Dijkstra but I think it is different. Can anyone help me with the time complexity of this algorithm.
vector<int> visited(N,0),dis(N,0);
vector<pair<int,int> > adj[N]; // value, node
void dijkstra()
{
for(int i=2;i<=N;i++)
dis[i]=N;
priority_queue<pair<int,int> ,vector<pair<int,int> >,greater<pair<int,int> > > pq;
pq.push(make_pair(0,1));
while(!pq.empty())
{
pair<int,int> p=pq.top();
ll x=p.second;
pq.pop();
if(p.first>dis[x])
continue;
for(int i=0;i<adj[x].size();i++)
{
if(dis[adj[x][i].ss]>dis[x]+adj[x][i].first)
{
dis[adj[x][i].second]=dis[x]+adj[x][i].first;
pq.push(make_pair(dis[adj[x][i].second],adj[x][i].second));
}
}
}
}

Arrays in C++ are zero based, that is the first index is 0 and the last is size()-1.
vector<int> visited(N,0),dis(N,0); <--- dis is initialized with N zero's
vector<pair<int,int> > adj[N]; // value, node
void dijkstra()
{
for(int i=2;i<=N;i++)
dis[i]=N; <---- initializing i=N or dis[N] is undefined behaviour
You write beyond the end of the array with possible disastrous results.
Your real error might be that that
dis[1] = 0
Where it should have been N or MAX_INT.

This algorithm can run infinitely when there exists a graph of a particular pattern. p.first>dis[x] may not always be true and then it will not exit the loop.
I believe that is the only part which is changed from the original Dijkstra algorithm

Related

Is this Union Find really O(n) as they claim?

I am solving a problem on LeetCode:
Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence. You must write an algorithm that runs in O(n) time. So for nums = [100,4,200,1,3,2], the output is 4.
The Union Find solution to solve this is as below:
class Solution {
public:
vector<int> parent, sz;
int find(int i) {
if(parent[i]==i) return i;
return parent[i]=find(parent[i]);
}
void merge(int i, int j) {
int p1=find(i);
int p2=find(j);
if(p1==p2) return;
if(sz[p1]>sz[p2]) {
sz[p1]+=sz[p2];
parent[p2]=p1;
} else {
sz[p2]+=sz[p1];
parent[p1]=p2;
}
}
int longestConsecutive(vector<int>& nums) {
sz.resize(nums.size(),1);
parent.resize(nums.size(),0);
iota(begin(parent),end(parent),0);
unordered_map<int, int> m;
for(int i=0; i<nums.size(); i++) {
int n=nums[i];
if(m.count(n)) continue;
if(m.count(n-1)) merge(i,m[n-1]);
if(m.count(n+1)) merge(i,m[n+1]);
m[n]=i;
}
int res=0;
for(int i=0; i<parent.size(); i++) {
if(parent[i]==i && sz[i]>res) {
res=sz[i];
}
}
return res;
}
};
This gets accepted by the OJ (Runtime: 80 ms, faster than 76.03% of C++ online submissions for Longest Consecutive Sequence), but is this really O(n), as claimed by many answers, such as this one? My understanding is that Union Find is an O(NlogN) algorithm.
Are they right? Or, am I missing something?
They are right. A properly implemented Union Find with path compression and union by rank has linear run time complexity as a whole, while any individual operation has an amortized constant run time complexity. The exact complexity of m operations of any type is O(m * alpha(n)) where alpha is the inverse Ackerman function. For any possible n in the physical world, the inverse Ackerman function doesn't exceed 4. Thus, we can state that individual operations are constant and algorithm as a whole linear.
The key part for path compression in your code is here:
return parent[i]=find(parent[i])
vs the following that doesn't employ path compression:
return find(parent[i])
What this part of the code does is that it flattens the structure of the nodes in the hierarchy and links each node directly to the final root. Only in the first run of find will you traverse the whole structure. The next time you'll get a direct hit since you set the node's parent to its ultimate root. Notice that the second code snippet works perfectly fine, but it just does redundant work when you are not interested in the path itself and only in the final root.
Union by rank is evident here:
if(sz[p1]>sz[p2]) {...
It makes sure that the node with more children becomes the root of the node with less children. Therefore, less nodes need to be reassigned a new parent, hence less work.
Note: The above was updated and corrected based on feedback from #Matt-Timmermans and #kcsquared.

I don't know what is wrong in this code for dijkstra algorithm. It shows error in the for loop line

vector<int> Dijkstra(int source,int destination)
{
priority_queue<fpair,vector<fpair>,greater<fpair>> pq;
vector<int> dist;
vector<bool> vis;
int n=flightMap.size();
dist.resize(n+1,1e7);
vis.resize(n+1,false);
vector<int> parent;
vector<int> path;
parent.resize(n+5,-1);
path.resize(n+5,-1);
dist[source]=0;
pq.push({0,source});
while(pq.size()>0)
{
auto[currw,currv]=pq.top();
pq.pop();
if(vis[currv])continue;
vis[currv]=true;
//if(dist[cv]<cw)continue;
for(auto[nextw,nextv,nextfin]:flightGraph[currv])
{
if(vis[nextv])continue;
int newWeight=currw+nextw;
if(newWeight<dist[nextv])
{
dist[nextv]=newWeight;
parent[nextv]=currv;
path[nextv]=nextfin;
pq.push({newWeight,nextv});
}
}
}
vector<int> pathTaken;
if(dist[destination]==1e7)return pathTaken;
for(int i=destination;i!=source;i=parent[i])
{
pathTaken.push_back(path[i]);
}
reverse(pathTaken.begin(), pathTaken.end());
return pathTaken;
}
I want to know how to tackle this problem, there is red line in the for loop indicating the error. priority queue is used to have current vertex and its weight. Two vector arrays visited and distance are also declared.I can not understand what auto[currw,cuurv] is doing here as it is throwing an error we should it through currw=pq.top().first. Secondly the major issue is in the for loop where auto[nextw,nextv,nextfin] is throwing an error. But I didn't find a way to solve this problem like I previously did.
Message:
Error:auto type cannot appear in top level array-type.

I am trying to make prims algorithm using c++ STL. After the iterator iterates 1st time it stops the code and gives wrong output

First I have created an un-directed graph of vertices, edges and weights.
In prims() function:
vertex[] array is initialized to INT_MAX for all indices i < n except 0 index. It will have smallest weights found till now.
bool isthere[] array to check either a vertex is visited or not.
list<int> s at first will have 5 (0-4 indices) values. After each for loop its value will pop.
vector<int> mset will keep the vertex chosen according to their smallest weight.
#include<bits/stdc++.h>
using namespace std;
void addEdge(vector<pair<int,int>>adj[],int u,int v,int wt){
adj[u].push_back(make_pair(v,wt));
adj[v].push_back(make_pair(u,wt));
}
void print(vector<pair<int,int>>adj[],int v){
for(int i=0;i<v;++i){
cout<<i<<"-->";
vector<pair<int,int>>::iterator it;
for(it=adj[i].begin();it!=adj[i].end();++it){
cout<<it->first<<"-"<<it->second<<" ";
}
cout<<"\n";
}
}
void prims(vector<pair<int,int>>adj[],int v){
int vertex[v];
bool isthere[v];
vertex[0]=0;
isthere[0]=true;
list<int>s;
s.push_back(0);
for(int i=1;i<v;++i){
vertex[i]=INT_MAX;
isthere[i]=false;
s.push_back(i);
}
vector<int>mset;
int i=0;
while(!s.empty()){
isthere[i]=true;
mset.push_back(i);
s.pop_back();
cout<<"i="<<i<<" ";
int lesser=INT_MAX;
vector<pair<int,int>>::iterator it;
for(it=adj[i].begin();it!=adj[i].end();++it){
cout<<"it-"<<it->first<<" "<<it->second<<"\n";
if(isthere[it->first]==false && vertex[it->first]>it->second){
if(lesser>it->second){
lesser=it->second;
i=it->first;
cout<<"i="<<i<<" ";
}
vertex[it->first]=it->second;
}
}
}
}
int main(){
int v=5;
vector<pair<int,int>>adj[v];
addEdge(adj,0,1,2);
addEdge(adj,0,2,8);
addEdge(adj,1,3,21);
addEdge(adj,4,1,6);
addEdge(adj,2,1,0);
addEdge(adj,2,4,5);
addEdge(adj,3,4,9);
print(adj,v);
prims(adj,v);
return 0;
}
Here's my adjacency list. It is an array of vector of pairs denoting vertex, weight.
0-->1-2 2-8
1-->0-2 3-21 4-6 2-0
2-->0-8 1-0 4-5
3-->1-21 4-9
4-->1-6 2-5 3-9
Here's the debug output and the error I got in the prims() function.
i=0 it-1 2
i=1 it-2 8
it-0 0
it- -874898181 134251312
Process returned -1073741819 (0xC0000005) execution time : 0.835 s
Press any key to continue.
for(it=adj[i].begin();it!=adj[i].end();++it){
// ...
i=it->first;
// ...
}
This code exhibits undefined behavior, as it compares an iterator into one container with iterator into a different one. it is initialized with, say, adj[0].begin(), then i changes inside the loop, and on the next iteration the iterator is compared with, say, adj[1].end()

Inconsistency between int and bool

I just implemented breadth first search in c++ and instead of declaring a vector as bool, I declared it as an int. This lead to a very odd observation. When I used int, the code printed the following:
1
32763
-524268732
Throughout the entire code, I don't provide any such value to variable as the 2nd and 3rd node receive, so I assume that they are just garbage values, but why do garbage values even come up, when I'm initialising the vector to be full of zeroes ??? You may check the code to be that below:
#include <iostream>
#include <queue>
using namespace std;
queue<int> neigh;
vector< vector<int> > graph(3);
vector<int> flag(3, 0);
int main(void)
{
graph[0].push_back(1); graph[0].push_back(2);
graph[1].push_back(0); graph[1].push_back(2);
graph[2].push_back(0); graph[3].push_back(1);
neigh.push(0);
while(!neigh.empty())
{
int cur = neigh.front();
neigh.pop();
flag[cur] = 1;
for(int i = 0, l = graph[cur].size();i < l;i++)
{
if(!flag[graph[cur][i]])
neigh.push(graph[cur][i]);
}
}
for(int i = 0;i < 3;i++)
{
cout << flag[i] << endl;
}
}
Alright, then I changed just a single line of code, line number 7, the one where I declare and initialise the flag vector.
Before:
vector<int> flag(3, 0);
After:
vector<bool> flag(3, false);
And voila! The code started working:
1 //The new output
1
1
So, my question is, what is the problem with the code in the first place ? I believe it may be some kind of error I made, or possibly that its only by chance that my bfs implementation works at all... So, what is the truth, SO? What is my (possible) mistake ?
You are accessing your vector out of bounds here:
graph[3].push_back(1);
At this moment, graph only has three elements. This leads to undefined behaviour.

Implementation and Improvability of Depth First Search

I have coded DFS as the way it is on my mind and didn't referred any Text book or Pseudo-code for ideas. I think I have some lines of codes that are making unnecessary calculations. Any ideas on reducing the complexity of my algorithm ?
vector<int>visited;
bool isFound(vector<int>vec,int value)
{
if(std::find(vec.begin(),vec.end(),value)==vec.end())
return false;
else
return true;
}
void dfs(int **graph,int numOfNodes,int node)
{
if(isFound(visited,node)==false)
visited.push_back(node);
vector<int>neighbours;
for(int i=0;i<numOfNodes;i++)
if(graph[node][i]==1)
neighbours.push_back(i);
for(int i=0;i<neighbours.size();i++)
if(isFound(visited,neighbours[i])==false)
dfs(graph,numOfNodes,neighbours[i]);
}
void depthFirstSearch(int **graph,int numOfNodes)
{
for(int i=0;i<numOfNodes;i++)
dfs(graph,numOfNodes,i);
}
PS: Could somebody please sent me a link teaching me how can to insert C++ code with good quality. I've tried syntax highlighting but it didn't work out.
Your DFS has O(n^2) time complexity, which is really bad (it should run in O(n + m)).
This line ruins your implementation, because searching in vector takes time proportional to its length:
if(std::find(vec.begin(),vec.end(),value)==vec.end())
To avoid this, you can remember what was visited in an array of boolean values.
Second problem with your DFS is that for bigger graph it will probably cause stack overflow, because worst case recursion depth is equal to number of vertices in graph. Remedy to this problem is also simple: use std::list<int> as your own stack.
So, code that does DFS should look more or less like this:
// n is number of vertices in graph
bool visited[n]; // in this array we save visited vertices
std::list<int> stack;
std::list<int> order;
for(int i = 0; i < n; i++){
if(!visited[i]){
stack.push_back(i);
while(!stack.empty()){
int top = stack.back();
stack.pop_back();
if(visited[top])
continue;
visited[top] = true;
order.push_back(top);
for(all neighbours of top)
if(!visited[neighbour])
stack.push_back(neighbour);
}
}
}