2

EDIT: [SOLVED]

HAVE A LOOK AT MY ANSWER ON THIS PAGE


Alright so in my endeavour to implement a O(N.lgN) Dijkstra solution in JAVA, i realised that first of all, it was hard to create tuples for Adjacent vertices and Weights for each vertex. This can easiy be done in C++ using:

pair<int adv_vertex,int weight>   

If you have no idea what i'm talking about, have a look at this solution: http://qr.ae/LoESY

Now, against all odds, i found a container similar to 'pair<>' in JAVA. As an example for(1,1), its declared as:

Map.Entry<Integer, Integer> pair=new AbstractMap.SimpleEntry<Integer,Integer>(1,1);

Read the details here: https://stackoverflow.com/a/11253710/4258892

Now, I succeeded in implementing a Dijkstra Algorithm using this DataStructure. I used a TreeSet with a custom comparator. This pretty much works like a PriorityQueue which extracts vertices having least weight first. This question can be considered as an extended version of:

Having trouble Implementing a Custom Comparator for a TreeSet (Dijkstra's)

After everthing was done, I tried my hand at this problem:

http://www.spoj.com/problems/EZDIJKST/

It clears the sample testcases but I get a TLE()TIme Limit Exceeded) on submission. So much for implementing a Dijkstra like no one ever has.

Here is the code that got TLE: http://ideone.com/wAQfBu

And here is my actual Dijkstra implementation with Explanation. The one I submitted to SPOJ is just without the comments and the output prompts. It could take a while for you to get your head around. I've tried commenting where and when possible.

import java.io.*;
import java.util.*;

    /**
     * Created by Shreyans on 3/25/2015 at 7:26 PM using IntelliJ IDEA (Fast IO Template)
     */

    class DIJKSTA_TRY
    {
        public static void main(String[] args) throws Exception
        {
            InputReader in = new InputReader(System.in);
            //Initializing Graph

            //AbstractMap.SimpleEntry<Integer,Integer> is similar to pair<int a,int b> in C++

            List<ArrayList<AbstractMap.SimpleEntry<Integer,Integer>>>gr=new ArrayList<ArrayList<AbstractMap.SimpleEntry<Integer, Integer>>>();//AbstractMap.SimpleEntry<Integer,Integer> is similar to pair<int a,int b> in C++
            System.out.println("Enter no of Vertices");
            int v=in.readInt();
            System.out.println("Enter no of Edges");
            int e=in.readInt();
            Set<Integer>vertices=new HashSet<Integer>();
            for(int i=0;i<=v;i++)//Initializing rows for each vertex
            {
                vertices.add(i);
                gr.add(new ArrayList<AbstractMap.SimpleEntry<Integer, Integer>>());
            }
            vertices.remove(0);//Since 0 was added in advertantly
            System.out.println("Enter <Vertex> <Adjacent Vertex> <Weight>");
            for(int i=0;i<e;i++)
            {
                int a = in.readInt();
                int b = in.readInt();
                int c = in.readInt();
                gr.get(a).add(new AbstractMap.SimpleEntry<Integer, Integer>(b, c));
            }
            System.out.println("Enter Source");
            int s=in.readInt();
            Comparator<AbstractMap.SimpleEntry<Integer, Integer>> comparator=new WeightComparator();
            TreeSet<AbstractMap.SimpleEntry<Integer, Integer>>ts=new TreeSet<AbstractMap.SimpleEntry<Integer, Integer>>(comparator);
            int[]d=new int[v+1];
            Arrays.fill(d,Integer.MAX_VALUE);//Initial distance INFINITY
            d[s]=0;//Setting distance of source from source 0
            vertices.remove(s);
            for(AbstractMap.SimpleEntry<Integer, Integer> pair:gr.get(s))//Storing vertices adjacent to source
            {
                ts.add(pair);
                d[pair.getKey()]=pair.getValue();
            }
            while(!vertices.isEmpty()&&!ts.isEmpty())
            {
                AbstractMap.SimpleEntry<Integer, Integer> curr=ts.pollFirst();//Removing that element
                int V=curr.getKey();//Got adjacent vertex;
                int W=curr.getValue();//Got weight
                //vertices.remove();
                for(AbstractMap.SimpleEntry<Integer, Integer> pair:gr.get(V))//Now traversing vertives adjacent to V
                {
                    int v1=pair.getKey();
                    int w1=pair.getValue();
                    if(d[v1]>W+w1)//setting distance if shorted
                    {
                        d[v1]=W+w1;
                    }
                    ts.add(pair);//Adding to TreeSet
                }
                vertices.remove(V);//Removing V from Vertices set
            }
            System.out.println("Single Source Shortest Distance from Vertex "+(s)+" is:");
            for(int i=1;i<=v;i++)
            {
                System.out.println("Shortest Distance from Source Vertex "+s+" is: "+d[i]);
            }
        }

        static public class WeightComparator implements
                Comparator<AbstractMap.SimpleEntry<Integer, Integer>>
        {
            @Override
            public int compare(AbstractMap.SimpleEntry<Integer, Integer> one,
                               AbstractMap.SimpleEntry<Integer, Integer> two)
            {
                return Integer.compare(one.getValue(), two.getValue());
            }
        }
        //**FAST IO. THAT'S IT. NOTHING DOWN HERE**
        private static class InputReader
        {
            private InputStream stream;
            private byte[] buf = new byte[1024];
            private int curChar;
            private int numChars;
            private SpaceCharFilter filter;

            public InputReader(InputStream stream)
            {
                this.stream = stream;
            }

            public int read()
            {
                if (numChars == -1)
                    throw new InputMismatchException();
                if (curChar >= numChars)
                {
                    curChar = 0;
                    try
                    {
                        numChars = stream.read(buf);
                    } catch (IOException e)
                    {
                        throw new InputMismatchException();
                    }
                    if (numChars <= 0)
                        return -1;
                }
                return buf[curChar++];
            }

            public int readInt()
            {
                int c = read();
                while (isSpaceChar(c))
                    c = read();
                int sgn = 1;
                if (c == '-')
                {
                    sgn = -1;
                    c = read();
                }
                int res = 0;
                do
                {
                    if (c < '0' || c > '9')
                        throw new InputMismatchException();
                    res *= 10;
                    res += c - '0';
                    c = read();
                } while (!isSpaceChar(c));
                return res * sgn;
            }

            public String readString()
            {
                int c = read();
                while (isSpaceChar(c))
                    c = read();
                StringBuilder res = new StringBuilder();
                do
                {
                    res.appendCodePoint(c);
                    c = read();
                } while (!isSpaceChar(c));
                return res.toString();
            }

            public double readDouble()
            {
                int c = read();
                while (isSpaceChar(c))
                    c = read();
                int sgn = 1;
                if (c == '-')
                {
                    sgn = -1;
                    c = read();
                }
                double res = 0;
                while (!isSpaceChar(c) && c != '.')
                {
                    if (c == 'e' || c == 'E')
                        return res * Math.pow(10, readInt());
                    if (c < '0' || c > '9')
                        throw new InputMismatchException();
                    res *= 10;
                    res += c - '0';
                    c = read();
                }
                if (c == '.')
                {
                    c = read();
                    double m = 1;
                    while (!isSpaceChar(c))
                    {
                        if (c == 'e' || c == 'E')
                            return res * Math.pow(10, readInt());
                        if (c < '0' || c > '9')
                            throw new InputMismatchException();
                        m /= 10;
                        res += (c - '0') * m;
                        c = read();
                    }
                }
                return res * sgn;
            }

            public long readLong()
            {
                int c = read();
                while (isSpaceChar(c))
                    c = read();
                int sgn = 1;
                if (c == '-')
                {
                    sgn = -1;
                    c = read();
                }
                long res = 0;
                do
                {
                    if (c < '0' || c > '9')
                        throw new InputMismatchException();
                    res *= 10;
                    res += c - '0';
                    c = read();
                } while (!isSpaceChar(c));
                return res * sgn;
            }

            public boolean isSpaceChar(int c)
            {
                if (filter != null)
                    return filter.isSpaceChar(c);
                return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
            }

            public String next()
            {
                return readString();
            }

            public interface SpaceCharFilter
            {
                public boolean isSpaceChar(int ch);
            }
        }

        private static class OutputWriter
        {
            private final PrintWriter writer;

            public OutputWriter(OutputStream outputStream)
            {
                writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
            }

            public OutputWriter(Writer writer)
            {
                this.writer = new PrintWriter(writer);
            }

            public void print(Object... objects)
            {
                for (int i = 0; i < objects.length; i++)
                {
                    if (i != 0)
                        writer.print(' ');
                    writer.print(objects[i]);
                }
            }

            public void printLine(Object... objects)
            {
                print(objects);
                writer.println();
            }

            public void close()
            {
                writer.close();
            }

            public void flush()
            {
                writer.flush();
            }
        }
    }

I was really stoked when i completed coding this. Took me almost 5 hours to find all the resources and put them together to make this but was highly disappointed when it couldn't clear the testcases in time.

Could someone please Suggest another way (Maybe a Data-Structure)/Optimization?

Also, what is the running time of this particular algo? What have you changed it to? (If you have)

Community
  • 1
  • 1
gabbar0x
  • 4,046
  • 5
  • 31
  • 51
  • Aren't you supposed to solve the problem yourself, since [spoj.com](http://www.spoj.com/) is a problem solving site with ranks and so on... ? – ericbn Apr 20 '15 at 20:42
  • I'm not asking for an answer to the problem. I already have it. I'm asking you how to optimize the above solution. IT'S EASY CODING IN A STANDARD ADJ MATRIX DIJKSTRA. If you can see at what level i have coded above, you will have no doubt on my ability to code the latter or about my knowledge – gabbar0x Apr 20 '15 at 20:44
  • And I am least concerned with ranks. I'm here to learn – gabbar0x Apr 20 '15 at 20:44
  • 3
    You should try to ask smaller questions. It's unlikely others will want to dig into that much of your code. At least you could remove all unrelated code (e.g. reading/writing). I can shortly advise that you should use OOP more, thus, tuple is a bad approach here, you should create another class, say, Edge to represent, well, an edge. Also you could check great videos by Tim Roughgarden, see chapter XI here: https://class.coursera.org/algo-004/lecture/preview – lagivan Apr 20 '15 at 20:49
  • 3
    Optimization is all about profiling, finding your bottlenecks, and then attacking those parts of the code. That's what you should do. Google "java profiling" and good luck! :- ) We'll be able to help you if you have a specific question, like "how can I make this line of code more efficient?" – ericbn Apr 20 '15 at 20:49
  • Yeah i get that. Thanks anyways :) I hope you get the spirit in which i asked the question. That's all – gabbar0x Apr 20 '15 at 20:51
  • **Could someone please optimise this?** are you serious? That is about as **off-topic** as it gets here ... especially with so many questions that actually have code optomizations and real questions about that code. –  Apr 20 '15 at 20:53
  • @JarrodRoberson - are you serious? pointing this as a duplicate to a **php question**? That is about as silly as it gets! – IVlad Apr 20 '15 at 20:54
  • What's duplicate about this? It's as original as it can get. And i CAN'T get it to run efficiently with the implementated datastructures. I'm simply asking for an alternative – gabbar0x Apr 20 '15 at 20:55
  • There are plenty of working examples posted on SO. Pick and choose what you want. Language is irrelevant when you have crap data structures and technique to begin with. If someone can optomize this in PHP then it should be trivial in Java or any other actual real language. –  Apr 20 '15 at 20:56
  • @JarrodRoberson no, the data structures are not crap here. It's the implementation. It's a valid question. – IVlad Apr 20 '15 at 20:56
  • 1
    Please give second thoughts before you trumple learners and small guys like me for no given reason. @JarrodRoberson. Your duplicate marking is STUPID. Please remove it. PHP? Really? – gabbar0x Apr 20 '15 at 20:56
  • Crap Datastructure? Hey mister, Why don't you uncrap it? Use your power to good use. – gabbar0x Apr 20 '15 at 20:57

3 Answers3

3
  1. Are you sure your IO functions are actually faster than what Java already provides?

  2. The code seems to be O((M + N) log N), with large constants though. First because you use a set and not a priority queue (like a binary heap) and second because of your AbstractMap.SimpleEntry which you create many instances of. And then because of your list of arraylists, which also might not be very fast.

In C++ you can get away with it more often than not (but not always even there; and in C++ Dijkstra is usually implemented using its priority queue implementation, not its set implementation). For Java, if you want to do online judge problems, I suggest you use existing classes such as the SimpleEntry as little as possible.

What I would do is write a binary heap implementation myself and use that instead of your sets, getting rid of all the SimpleEntries as well since you have more control with your own heap implementation and you can just using a simple class Pair { public int node; public int weight; } with no comparators.

If you don't want to go that far, maybe you can first try replacing your sets with Java's own PriorityQueue (be careful to use the right, O(log N) remove method!).

IVlad
  • 43,099
  • 13
  • 111
  • 179
  • I hear you. The C++ guys do get away with it. Anyways do have any reference from where I can pick it up? Or could you code it in and I guess if it works this question will be done with – gabbar0x Apr 20 '15 at 21:07
  • @bholagabbar I don't have time to write one unfortunately, but you should be able to find binary heap implementations in Java. A quick search came up with this for example: http://stackoverflow.com/questions/18241192/implement-heap-using-a-binary-tree do try Java's priority queue first though, it should be quicker. – IVlad Apr 20 '15 at 21:09
  • About the IO. A lot of good JAVA coders use this template. I picked it up too. Its almost as fast as BufferedReader and Scanner doesn't stand a chance. However unlike BufferedReader, its easier to take input. I don't have to take it line by line – gabbar0x Apr 20 '15 at 21:09
  • @bholagabbar ah, I see. I know people parse input even in C with `fread` on online judges, but I wasn't familiar with the approach in Java. If it's fast, that's great. – IVlad Apr 20 '15 at 21:11
  • Anyways thanks! Your answer has been really helpful. I'll try implementing the PriorityQueue and if that doesn't work then ill try the binary heap. Hopefully the combination should pass – gabbar0x Apr 20 '15 at 21:12
  • Hey @IVlad, could you have a look at my updated answer? Tell me if it's legit? I did the best I could manage – gabbar0x Apr 21 '15 at 13:23
2

This is not Dijstra's algorithm. Dijkstra's algorithm enumerates all shortest paths starting from the source, while your algorithm enumerates all paths starting from the source, even those containing cycles.

For instance, consider the graph

1 <----> 2 <----> 3 <----> 4 <----> 5

Dijstra would enumerate the following paths:

[1]
[1,2]
[1,2,3]
[1,2,3,4]
[1,2,3,4,5]

while your algorithm enumerates

[1]
[1,2]
[1,2,1]
[1,2,3]
[1,2,1,2]
[1,2,3,2]
[1,2,3,4]
[1,2,1,2,1]
[1,2,1,2,3]
[1,2,3,2,1]
[1,2,3,2,3]
[1,2,3,4,3]
[1,2,3,4,5]

... and if the graph was any larger, the difference would be even more striking. For instance, consider the graph where the set of vertices are the integers, and two vertices x,y are adjacent if and only if x + 1 = y or x = y + 1. Let 0 be source, and n be the target. By construction, the shortest path from 0 to n has length n. Dijkstra will visit all nodes from -n to n exactly once. Your algorithm will visit all paths of length at most n, of which there are 2^n (including paths like 0,1,0,1,0,1,0,1,0,1,....). For n = 20, Dijkstra will visit 41 paths, while your code will visit about a million!

Therefore, before you fiddle with fast I/O routines, or try to fiddle with faster trees, get your algorithm right!

PS: There are also a couple of bugs that make your implementation incorrect besides being slow. For instance, if we have a path of length n, and add a step of length s, the new path should have length n + s, not s ...

meriton
  • 68,356
  • 14
  • 108
  • 175
1

Alright FINALLY! So here's a CLEAN and (hopefully) FAST Dijkstra's implementation.

As suggested by IVlad and few others, instead of using:

AbstractMap.SimpleEntry

I created a custom datatype called 'Node' which stores 2 integer tuples

Node x=new Node(AdjacentVertex,Weight) 

I implemented a comparator for this 'Node' class which sorts it according to weights assigned. Less weight-> More Priority.

And Finally, I succeeded in implementing a Priority Queue which stores objects of 'Node' according to the above order which the Comparator sets.

Queue<Node> pq=new PriorityQueue<Node>(new Node());

I got my solution AC (Accepted) on SPOJ: http://www.spoj.com/status/EZDIJKST,bholagabbar/

Here is the SPOJ code (Same code as below but with faster IO & no prompt statements): http://ideone.com/p7u5vN

And cutting to the chase, Here is my Final, Simple, 'Proper' and Original Implemetataion:

import java.util.*;

/**
 * Created by Shreyans on 4/21/2015 at 6:32 PM using IntelliJ IDEA (Fast IO Template)
 */

class DIJKSTRA
{
    public static void main(String[] args) throws Exception
    {
        Scanner sc=new Scanner(System.in);
        List<ArrayList<Node>> gr=new ArrayList<ArrayList<Node>>();//Initialising Adj list to store graph
        System.out.println("Enter Number of Vertices");
        int v=sc.nextInt();
        for(int i=0;i<=v;i++)
        {
            gr.add(new ArrayList<Node>());
        }
        System.out.println("Enter Number of Edges");
        int e=sc.nextInt();
        System.out.println("Enter <Vertex> <Adjacent Vertex> <Weight>");
        for(int i=0;i<e;i++)
        {
            int a=sc.nextInt();
            int b=sc.nextInt();
            int c=sc.nextInt();
            gr.get(a).add(new Node(b,c));
        }//Built Graph
        System.out.println("Enter Source");
        int s=sc.nextInt();
        //int des=sc.nextInt();//Entering Destination
        Queue<Node> pq=new PriorityQueue<Node>(new Node());//Heap to extract value
        boolean[]checked=new boolean[v+1];//Keeping track of checked values
        int[]d=new int[v+1];//Keeping track of distances
        Arrays.fill(d,Integer.MAX_VALUE);
        d[s]=0;
        pq.clear();
        pq.offer(new Node(s,0));
        while(!pq.isEmpty())
        {
            Node x=pq.poll();
            int V=x.node;//Getting next node from heap
            int W=x.cost;//Getting cost
            checked[V]=true;
            for(int i=0;i<gr.get(V).size();i++)
            {
                Node z=gr.get(V).get(i);//Getting all adjacent Vertices
                if(!checked[(z.node)])//Not checking visited Vertices
                {
                    int v1=z.node;
                    int w1=z.cost;
                    if(d[v1]>W+w1)//Checking for min weight
                    {
                        d[v1]=W+w1;
                    }
                    pq.offer(new Node(v1,d[v1]));//Adding element to PriorityQueue
                }
            }
        }
        for(int i=1;i<=v;i++)//Printing Shortest Distances. Ignore ones with Integer.MAV_VALUE. Meh
        {
            if(d[i]==Integer.MAX_VALUE)
            {
                System.out.println("No Path connecting Source Vertex "+s+" to Vertex "+i);
            }
            else
            {
                System.out.println("Shortest distance from  Source Vertex "+s+" to Vertex "+i+" is: "+d[i]);
            }
        }
    }
}
class Node implements Comparator<Node>
{
    public int node;
    public int cost;

    public Node(){}

    public Node(int node, int cost)
    {
        this.node = node;
        this.cost = cost;
    }

    @Override
    public int compare(Node node1, Node node2)
    {
        if (node1.cost < node2.cost)
            return -1;
        if (node1.cost > node2.cost)
            return 1;
        return 0;
    }
}

Could anyone tell me what's The EXACT Time and Space Complexity of THIS implementation? I need these details for a project I'm doing. Can my implementation be improved further? Suggestions most welcome

gabbar0x
  • 4,046
  • 5
  • 31
  • 51
  • 1
    Congratulations, I'm glad it works. The complexity is still what I mentioned in my answer. Space complexity is `O(N + M)`. You can still improve it a little: get rid of `ch` for example, you can do the same thing usind `d`: if `d[node] == Integer.MAX_VALUE`, then `node` has not been visited. No need for a hash set. You can add the same check in your last for loop to avoid those in your comment. – IVlad Apr 21 '15 at 14:24
  • I removed the HashSet and replaced it with a boolean array like boolean[]checked=new boolean[v+1]. So now the searching for the vertices part is less. Should speed up a little – gabbar0x Apr 21 '15 at 18:43