Recursion

This is a simple concept and yet scares many, as a simple mistake can bring down even a supercomputer by making infinite recursive calls and run out of resources. Also it is hard to think recursively as we can’t easily relate to any of the real world elements. It is not too complex topic to understand like 5th dimension and by the way, the dimensions topic is pretty interesting too.

Why should I learn recursion?

It is one of the key concepts and following are some of the algorithms and problem spaces which we can easily solve using recursion –

Hoping now that you are motivated to understand recursion lets start by discussing an example.

What is recursion?

Let us assume you bought a box of chocolates to a classroom and want to give it to everyone in the class. One intuitive and simple way is to go to each person and give a chocolate. This will surely make us work hard and more the members in classroom the more work we do. Is there a better way?
What about, you take a chocolate for yourself and give the box to the person next to you and say, can you take one and pass on to the next person? He simply can take one chocolate for himself and then do the same thing – giving it to next person.
Above problem can be classified as recursion – distributing chocolates. Taking a chocolate is doing some work and then simply recurse for remaining persons in the classroom. One of the important thing is, this will stop when everyone gets a chocolate. By the way, this is called the base case.
Following are the two important things to remember when we write recursive functions,

  • It should contain a base case. That means, we should have a small enough problem where we can solve it directly and simply stop recursing further. In above example, last person simply takes the chocolate for himself and stops as everybody already got one.
  • The problem should become smaller and smaller for each recursive call. In the above example, first we need to give it to all and next it is all-1, all-2, all-3 and so on.

Let us look at the simple programming example – print “Hello World!” 10 times.
The intuitive way is, loop through 10 times and print the statement “Hello World!” but assume that we have a constraint of not to use loop construct. What about, we print “Hello World!” and then recursively say, can you print 9 more times, print and recurse for 8 more times, print and recurse 7 more times and so on till we reach printing only 1 time. This can be written as follows,

    public static void printHelloWorld(int times) {
        if (times == 0) {
            return; // base case
        } else {
            System.out.println("Hello World!");
            printHelloWorld(times - 1); // recursive call
        }
    }

The above can also be written simply as,

    // implicit base case
    public static void printHelloWorld(int times) {
        if (times > 0) {
            System.out.println("Hello World!");
            printHelloWorld(times - 1); // recursive call
        }
    }

The best way to get comfortable with this topic is to solve as many recursive problems as possible. Let us look into some of the problems below and see how can we solve them with recursion.

POW function

Given a base and exponent, how do we raise the wbase to the given exponent. Iteratively, we can solve this by simply using a loop construct and multiply base exponent times. Can we do this recursively? Let us take an example – base is 2 and exponent is 10. This can also be written as 2 * (2 raised to 9). So that means, if we know the value of base raised to (exponent – 1) times, then we can simply multiply it with base once and we get what we want. If you look at this approach carefully, raising base to (exponent – 1) times is same problem and so we can easily form this into recursive formula – base raise to exponent  =  base * (base raised to exponent -1). That is pow(base, exponent) = base * pow (base, exponent -1). The base case occurs when exponent is 0 and in that case, we simply return 1. Following is the java code,

    public static double pow(double base, double power) {
        if (power <= 0) {
            return 1.0;
        } else {
            return base * pow(base, power - 1);
        }
    }
Power function

Recursive call stack for POW function

A visual way to look at the above is for – raise 2 to 5,

Just FYI, if you carefully observe it, we can do little optimization and reduce the run time of the above from O(n) to O(log n). The recursive formula can also be written as,

  • If exponent is even, then pow(base, exponent) = pow(base, exponent/2) * pow(base, exponent/2)
  • if exponent is odd, then pow(base, exponent) = base * pow(base, exponent/2) * pow(base, exponent/2)

Optimized java code for POW function is,

    public static double power(double x, int n) {
        if (n == 0) {
            return 1;
        } else {
            double v = power(x, n / 2);
            if (n % 2 == 0) {
                return v * v;
            } else {
                return v * v * x;
            }
        }
    }

By the way, the above code does not work for negative exponents and you can take that as an exercise.
Hint – write a wrapper function to check if exponent is negative or positive and then I hope you know what to do.

Palindrome

Given a string input, find if it is a palindrome or not. As per Wikipedia – “A palindrome is a word, phrase, number, or other sequence of characters which reads the same backward or forward”. One other way to think of this is, take first and last character and compare. If they are equal then remove them and check for inner string. If at any point the first and last character is not same, it is not a palindrome otherwise it is. The recursive formula is,

  • palindrome(INPUT) = (first character == last character ) and palindrome(INPUT after first and last characters removed)

The better way to understand is to visualize it.

recursion madam word

Comparisons for “MADAM” word

recursion madam 2

Recursive call stack for “MADAM” palindrome method

Java code for implementing palindrome recursively is,

    public static boolean isPalindromeRecursively(String input) {
        if (input.length() <= 1) {
            // one character or zero characters string is palindrome.
            return true;
        } else {
            char firstChar = input.charAt(0);
            char lastChar = input.charAt(input.length() - 1);
            // both end chars are same and inner input is palindrome, then it is palindrome.
            return (firstChar == lastChar) && isPalindromeRecursively(input.substring(1, input.length() - 1));
        }
    }

Note,

  • The above code is not efficient as it creates many substrings and the better way to write it by converting the input to array and use start and end indexes.
  • It might have few more constraints like ignoring special characters and spaces. For example, “A man, a plan, a canal — Panama!” might be considered as a palindrome but above code returns false.

A good exercise for you would be to take the above constraints and write/modify the code.

Binary search

This is one of the best algorithms to know and can be written very easily using recursion. The problem is, we are given a list of sorted integers (for the sake of simplicity) and a number which we need to search for. The brute force algorithm is to simply scan the elements from start to finish and see if we have the number which we are searching for. We can take the advantage of the sorted property and do much better as follows.

  • check the mid element
    • if it is the one which we are looking for then we are done
    • If not,
      • if it is greater than what we are looking for, then recursively search in the left half as all elements on right will be greater and there is no way we can find what we are looking for as the array is sorted
      • If it is less than what we are looking for, then search in the right half as all elements in the left half are smaller
    • if neither of the above are true then that element does not exist in this – not found.
Recursion - Binary Search

Recursion – Binary Search comparisons

Following is the java code for binary search algorithm,

    public static int search(int[] input, int element, int start, int end) {
        if(end < start) return - 1;
        int mid = (start + end)/2;
        if(input[mid] == element) return mid;
        if(element < input[mid]) {
            return search(input, element, start, mid -1);
        } else {
            return search(input, element, mid + 1, end);
        }
    }

Summary

There are many different kinds of problems which we can resolve and most of them tend to following these patterns –

  • Dive into half and recurse on left or right (sometimes both) half – like binary search
  • Solve first/last and then recurse on remaining
  • Choose one or make one decision and recurse on remaining one
  • Recurse first and then process – like power function as we need the value from recursive call to calculate the current value
  • Process and then recurse – like binary search and some of graph searching algorithms
  • Bottom up vs Top down recursion
  • Base case is important and also make sure every recursive call is made for smaller problem – otherwise we end up in infinite loop

Hope this blog gave you some insights on recursion and the best way to get comfortable with this is to practise as many problems as possible. Following are few other problems,

Graphs & Trees – Shortest Path (BFS, Dijkstra, Bellman Ford)

Before exploring this topic, it will help if you go through my previous blog on graph basics which covers the different types of graphs, their representations & different searching techniques.
The motivation behind the shortest paths are many, for example finding a quick route between two cities, search engine crawling, data transfer to destination over the network, finding relationships on Facebook, Linkedin and many more. Following are the few key elements which we need to keep in mind while implementing any algorithm on graphs.

  • The type of graph – Directed vs undirected, weights (+ve & -ve)
  • Possibility of cycles – one of the most important
  • Density of the graph – this will affect performance a lot
  • Do we really need an optimal path – in some cases, it will take a lot of time to calculate this and we might be happy with sub optimal path itself
  • And finally, the meaning/definition of shortest – number of edges or vertices, minimum weight in terms of weighted graphs etc.

Basically, if we don’t have any weights and the shorted path is the minimum number of edges/vertices, then it is a straightforward problem as we can use Breadth First Search or Depth First Search algorithm to find it. The only additional information which we need to keep in track while doing BFS/DFS is the predecessor information so that we can keep track of the shortest path.

Shortest Path – by BFS

Breadth First Search can be used to find the shortest paths between any two nodes in a graph. To understand BFS in more details, check this post
Following is the pseudo instructions for this algorithm.

  • start at the source node S
  • put this in current level bucket
  • while we have not ran out of graph (next level bucket is not empty) and not found our target node
    • take a node from current level bucket  –  call it C
    • for each node reachable from current node C – call it N
      • (we can also check to see if it is visited node and skip it to avoid the cycles)
      • mark the predecessor of N as C
      • Check if N is our target node (that is have we reached the target node)
        • If YES, then exit
    • put this N in next level bucket
    • mark next level bucket as current level

The code for this will be almost same as BFS except we keep track of predecessors so that we can build the path. For this, we can simply use a dictionary. The code for this can be found in my previous blog here.

Dijkstra’s algorithm

Dijkstra’s shortest path algorithm is mainly used for the graphs with +ve weights and in the case of finding single source shortest paths.
What does it mean by single source shortest path?
It means, given a source node (S), this algorithm will provide us shortest paths to all of the nodes in a graph which are reachable from S.
If we need a shortest path between two nodes, we simply stop(exit) executing this algorithm when we find the shortest path to the target node. It is sometimes very hard to find the stopping point for shortest path between two nodes, so we mostly perform full algorithm and then find the shortest path between nodes S and T. There might be also cases where we just need suboptimal solution (a reasonable shortest path) and in this case, we could save some good amount of execution time by simply stopping when we reach our expected suboptimal path. By the way, this algorithm is named after Edsger Dijkstra as he came up with this.

First let us look into an example on why we can’t use BFS for weighted graph. Consider the below graph without weights and let us assume we want to find a shortest path between source node S and target node T.
Shortest path by BFS
So in this case, we could end up with any one of the path shown above shortest paths from S to T. We don’t bother which path we choose as both are the shortest paths between S and T in terms of number of nodes in a path. Now let us assign some weights.
Graph with negative weights
The above diagram clearly tells us that both the paths by BFS (S–>A–>T & S–>B–>T) are not shortest and the actual shortest path is with the cost 8 and it is S –>B–>D–>T.
Based on this example, we can clear say that,

  • Path with less number of nodes might not be the shortest path when there are weights
  • BFS algorithm will not work in all of the cases when we have a graph with weights

Now let us look into the Dijkstra’s algorithm and see how it finds out the shortest path. It basically takes each node and explores all of the paths from that node by calculating the cost and predecessors and performs this till we find the shortest paths. It will be exponential if we simply do the above approach but Dijkstra’s algorithm uses the following two techniques to avoid exploration of exponential paths.

  • Relaxation
    • Initialize the path cost of all of the nodes to Infinity
    • Then whenever we reach a node, we take the minimum of (older cost, path cost of predecessor + cost between predecessor and this node)
  • Topological sort
    • The order of processing each node from graph hugely affects the total execution time. Will look into in detail in below sections on this.

Relaxation Technique

To explain this, let us consider the same example.
Graph with weights
There are many paths between S to T and for simplicity, let us assume that we reach node T in the below order.

  • S → A → T (cost: 12)
    • In this case, we are reaching T from A, so the total cost is S→ A path cost + cost from A → T and the cost is 10 + 2 = 12
    • So this path cost is MIN ( Infinite, 12) = 12. Note that we initialize the cost as Infinity. Update the predecessor of T as A.
  • S → B → T (cost: 10)
    • Total cost is cost of S to B + cost of B to T. Cost is 3 + 7 = 10
    • So this path cost is MIN(12, 10) = 10. Found a new path. Update the predecessor of T as B.
  • S → A → B → T (cost: 22)
    • Cost is MIN (10, 22) = 10. No change
  • S → B → D → T (cost: 8)
    • Cost is MIN (10, 8) = 8. Found a new path. Update the predecessor of T as D.

So once we done with the algorithm execution, we will have the shortest path from S to T as S→ B → D → T.

Topological sort

Now you might be wondering why we need a topological sort at all. Does the relaxation technique won’t sort out the problem completely? To answer this, we need to look into a well known example.
Graph - Topological sort In this, first let us calculate the weights from A to G by just taking the direct straight links. Then the shortest paths for the vertices B to G will be B-4, C-8, D-10, E-12, F13 & G-14. Now let us look at the relaxation. E → G, there is an edge with weight 1, so the shortest path will be 13 (A-E 12 and E to G 1 = 13). Now look at the edge from C to E. If we take that then the shortest path to E will become 10 (A → C 8, C → E 2), now the shortest path to E becomes 10, then we need to change the weights of F to 11 (from 13) and G to 11 (from 13). This is just an example where the dynamic paths can become exponential and it really hurts the performance of our shortest path algorithm.

Dijkstra uses a priority query which will help us picking up the vertices with minimum path which leads to a better topological sort.
Following is the pseudo instruction-
Dijkstra algorithm for shortest paths to all the vertices in a graph from source vertex ‘s’

  • Initialize a dictionary S which holds the shortest paths already calculated.
  • Add all of the vertices to a priority queue – Q.
  • Set the distance from s to s as zero and to all other vertices as infinite.
  • while the Queue is not empty
    • take a vertex ‘u’ from query – extract min
    • add u to S
    • for each vertex v which is adjacent to u,
      • relax u → v. relaxation step. Minimum (shortest path to u + weight of edge u to v, existing shortest path)

Finally, S contains the shortest paths to all of the nodes from graph from source vertex s.
Basically, it is a greedy algorithm. The topological sort it uses is the order of picking up a vertex from queue with minimum path weight.

Following is the pseudo code
G – graph, s – source, Q – priority query, dist – dictionary stores vertex and shortest path
function Dijkstra (G, s)

  • Q, dist = InitializeGraph(G, s)
  • while Q is not empty
    • u = Q.extract_min() // retrieves the vertex with minimum path distance.
    • mark u as visited
    • for each adjacent vertex v of u
      • if v is not yet visited
        • relax(u, v)

// initializes a graph and returns priority queue
function InitializeGraph(G, s)

  • dist[s] =0
  • for each v in G
    • if v not equal to s
      • dist[v] = infinity
    • Q.add(v, dist[v]) – priority queue

return Q, dist
function relax(u, v, Q)

  • newWeight = dist[u] +  weight from u to v
  • if newWeight < dist [v]
    • dist[v] = newWeight
    • Q.decrease_priority(v, newWeight)

Why does Dijkstra won’t work for a graph with negative edges?
The problem is the relaxation step and the condition of running the algorithm till we don’t have any edges which can be relaxed.
Let us look at the following graph and think for a few seconds why it will fail.
Graph with negative weights
Here, when we start at s (source),

  • we set s to s as 0 and all other (s → b & s → c) as infinite
  • Then we pull c, we relax that edge (min (infinity, 2)), mark c as visited
  • then we pull b, we relax that edge (min (infinity, 2)), mark b as visited.
  • No more edges, the algorithm is done and it says the shortest path from s to c is 2.

But in fact, the shortest path from s to c is s → b → C is -3. The simplest reason is, Dijkstra algorithm assumes that the weights will only increase. In this case, when it visited(pulled out from Q) c, it marked it as visited and never going to visit again as it assumes that shortest path to c from anywhere else will be more than 2.

Bellman-Ford

Now let us look into Bellman Ford algorithm and see how it solves the shortest path problem for a graph with negative weights. This algorithm closely follows the Dijkstra algorithm and uses relaxation technique. The only difference is, it executes this |V| – 1 times for each edge where |V| is the number of vertices in the graph.
There is also another aspect which it determines is the negative cycle paths. Let us look at the following example.
Belman ford  - Graph
In this example, can you determine the shortest path distance from B → D? No, because every time we cycle between B → C → D → B, it reduces by 1. So we can cycles through this infinite times and there is no way we can identify the shortest path from B → D in this graph. So in this case, Bellman-Form algorithm throws error saying it can’t find the short path.
Following is the pseudo code for Bellman-Ford
V[] – vertices, E[] – edges, s – source vertex
function Bellman-Ford(V[], E[], s)

  • // Initialize the graph
  • dist[] = InitializeGraph(V[], s)
  • for 1 to size(V) -1    // This is the diffrence from Dijkstra
    • for each edge e (u, v) in E
      • relax(u, v, dist[])
  • if haveNegativeCycles(E[], dist[])
    • throw new Error(“Graph have negative cycles”)

return dist

// initializes a graph
function InitializeGraph(V[], s)

  • for each vertex v in V[]
    • dist[v] = infinity
  • dist[s] = 0

return dist[]

// relaxation
function relax(u, v, dist[])

  • newWeight = dist[u] +  weight from u to v
  • if newWeight < dist [v]
    • dist[v] = newWeight

function haveNegativeCycles(E[], dist[])

  • for each edge e (u, v) in E
    • if dist[u] + weight from u → v < dist[v]
      • return true  // yes, if can find a edge to relax, then it contains negative cycles

return false // no negative cycles

So basically it follows the Dynamic Programming (DP) approach. When we relax all of the edges once, the graph (dist[]) will have shortest paths with 1 edge, after two cycles, we will have shortest paths with 2 edge paths and so on. After we perform this |V| – 1 times, we will have shortest paths from the source to all of the vertices and we simply throw the error when it contains negative cycles.

Thats all about shortest paths. These are just basics and we do have more complex ones with heuristics and NP-Hard problems like Traveling Salesman problem, but it will be very hard to describe them in a blog post.

Thank you for your time and feel free to provide your feedback through comments.

Graph & Tree algorithms

Graphs and Trees are at the heart of a computer and very useful in representing and resolving many real world problems like calculating the best paths between cities (Garmin, TomTom) and other famous ones like Facebook search and Google search engine crawling. If you know any programming language, you might have heard of garbage collection and for that the typical graph search called Breadth First Search is used. In this post, I will try to cover the following topics.

  • Graphs – directed, undirected and special case of directed which are called Directed Acyclic Graph(DAG)
  • Trees – Binary trees and also most useful version of it, the Binary Search Trees (BST)
  • Standard traversal techniques – Breadth First Search (BFS), Depth First Search (DFS).

I know it will be a bit lengthy but I assure you it will be worth going through. I will try to cover the following topics in another post.

  • Overview of shortest path algorithms – BFS, Dijkstra and Bellman Ford
  • BST Search, Pre-order, In-order and Post-order.

Graphs

A graph consists of vertices (nodes) and edges. Think vertex as a point or place and the edge is the line/path connecting two vertices. In a typical graph and tree nodes there will be some kind of information is stored and the vertices will be associated with some cost. There are mainly two types of graphs and they are undirected and directed graphs.

In undirected graphs, all of the edges are undirected which means we can travel in either direction. So if we have an edge between vertex A and B, then we can travel from A to B and also from B to A. Following is the sample undirected graph.

A Undirected Graph

A Undirected Graph

Here in this undirected graph,

  • The vertices are – A, B, C, D, E and F.
  • The edges are – {A, B}, {A, C}, {B, C}, {B, D}, {C, D}, {D, E}, {D, F} and {E, F}. Here edge {A, B} means, we can move from A to B and also from B to A.

In directed graphs, all of the edges are directed which means we can only travel in the direction of an edge. So for example, if we have an edge between vertices A and B, then we can only go from A to B and not from B to A. If we need to go from B to A, then there should be another edge which should point B to A. Following is the sample directed graph.

Directed Graph

Directed Graph

Here in this directed graph,

  • The vertices are – A, B, C, D, E and F.
  • The edges are – (A, C), (B, A), (B, D), (C, D), (D, E), (D, F) and (F, E). Here edge (A, C) means, we can only move from A to C and not from C to A.

The graphs can be mixed with both directed and undirected edges but let us keep this aspect away in this post to understand the core concepts better. Just an FYI the undirected edges are represented by curly braces ‘{}’ where as directed edges are represented by ‘()’.

Directed Acyclic Graphs – DAG

This is a variation of standard directed graph without any cycle paths. It means there is no path where we can start from a vertex and comeback to the same starting vertex by following any path. By the way, cycles in the graph are very important and we need to be very careful about it. Otherwise, we could end up writing infinite algorithms which will never be completed.

Graph_DAG_1

The above diagram does not have a cycle, so it is a DAG. Basically we can start from any node and won’t come back to the same starting node.

Graph_not_DAG_1

If we carefully look at the above listed directed graph, there is a cycle path and because of that, this graph is not a DAG. We can start from node A and come back to it by following the path.

Trees

This is another variation or we may call it subset of a graph. So we can say, all trees are graphs but not all graphs are trees. It is almost similar to the real world tree. It will have one top level vertex which is called root, and every node will have either zero or more child vertices. The best examples of tree data structure are family relationship and a company organization chart. Few important things to note down here,

  • There will not be any cycles in this.
  • Every node will have only one parent except the root which will not have any.

Following is a sample tree structure.

A Tree

A Tree

As a standard the tree is represented from top to bottom. In this, the top node A is called as root of this tree as there are no parents to it. Then all of the child vertices (B, C and D) of A are represented on next level. All of the nodes which don’t have any child nodes (E, F, G, H and D) are called leaf nodes.

DAG vs Tree

As you can see by now both the DAG and tree will not have any cycle paths but there is a key difference that is, in DAG there can be nodes with multiple parents but where as in tree, every node will have either a single parent or none. Following diagrams depicts this difference.

DAG vs Tree

DAG vs Tree

In the above DAG example, D has two parents B and C and this is not possible in the tree. In tree example above, D has just one parent C.

Binary Tree

This is a version of the tree structure and in this every node can have strictly at most two child nodes. That means a node either can have two child nodes, just one or simply no child nodes in which case we call them leaf nodes. Following is a sample binary tree.

Binary Tree

Binary Tree

Binary Search Tree (BST)

This is a most useful version of the Binary Tree in which every node (including root) holds the following two properties.

  • The left child node (if exists) should contain the value which is less than or equal to the parent.
  • The right child node (if exists) should contain the value which is greater than or equal to the parent.

The above ordering property is really useful which we can understand clearly when we go through some of the search algorithms (especially the Binary search).

For simplicity, we will use the numbers in the nodes to show this relationship but the nodes can contain any information as long they are comparable. That means we should be able to compare them in some way and tell, whether those are equal or whether which one is greater.  Following is a sample BST,

Binary Search Tree

Binary Search Tree

Data Structure representation

Hopefully all of the above details provide the insights on graphs and tree data structures. Before we get on to the actual traversals and searching algorithms, it is essential and useful to understand how they are represented or stored them in programming languages.

Following are the two mostly used representations.

Adjacency Lists

In this, the node is represented by an object and the edge is represented by the object references. Every object will have some data and a list of adjacent node references or a method which can return the list of adjacent nodes. If there are no neighbors, then it will be null or empty. So in nutshell, we have some mechanism to quickly find out neighbors for a given node.

There is also another way to represent using adjacency lists which having a map which contains the key as the node and the value is list containing all of its neighbors. In this case, we can simply get all of its neighbors of a node N by simply looking for a key N in the map. That is we could say the neighbors of node N are simply Adj[N] where Adj is the adjacency map. Following is a sample representation of this representation.

Graph Adjacency list representation

Graph Adjacency list representation

Matrix representation

In this, we will use a double dimensional array where rows and columns represent the nodes and will use some kind of special value or actual edge cost as the value if they are connected. Following is sample example of representation.

Graph matrix representation

Graph matrix representation

In the above, we have used 1 to represent the edge and 0 for no edge and we are looking from row to column. That is Array[B][C] is 1 as there is edge from B to C but Array[C][B] is zero as there is no edge between C to B. We could also use some value to represent the actual cost and use some sentinel (infinity or -1) value to represent no connection between the nodes. Yes, it is not a regulatory object oriented design but it have its own advantages like accessing random node and get the edge cost of a neighbor in constant time.

Searching Algorithms

There are two main search techniques which work fine on both the graphs and all versions of trees. It is just that we need to be careful about the cycles in graphs.

Breadth First Search (BFS)

In this, we will search or examine all of the sibling nodes first before going to its child nodes. In other words, we start from the source node (root node in trees) and visit all of the nodes reachable (neighboring nodes) from the source. Then we take each neighbor node and perform the same operation recursively until we reach destination node or till we run out of graph. The following color coded diagrams depicts this searching.

Breadth First Search

Breadth First Search

In this, the starting node is S and the target node is V.  In this,

  • Visit all of the nodes reachable from S. So we visit nodes A & C. These are the nodes reachable in 1 move or nodes at level 1.
  • Then visit the nodes reachable from A & C. So we visit nodes B, D & E. These are the nodes reachable in 2 moves or we can say nodes at level 2.
  • Then visit the nodes reachable from B, D & E. So we visit nodes F & V. These are the nodes reachable in 3 moves or we can say at level 3.

Advantages of this approach are,

  • The path from which we first time reach a node can be considered as shortest path if there are no weights/costs to the edges or they might be constant. This is called single source shortest paths.
  • This can be used in finding friends in social network sites like Facebook, Google+ and also linked in. In linked in, you might have seen the 1st, 2nd or 3rd degree as super scripts to the friend’s names – it means that they are reachable in 1 move, 2 moves and 3 moves from you and now you might guess how they calculate that J

BFS implementation

We can implement this in multiple ways. One of the most popular ways is by using Queue data structure and other by using lists to maintain the front and next tier nodes. Following is the java implementation of this algorithm.

  public static void graphBFSByQueue(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; source) {
    //if empty graph, then return.
    if (null == source) {
      return;
    }
    Queue&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; queue = new LinkedList&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //add source to queue.
    queue.add(source);
    visitNode(source);
    source.visited = true;
    while (!queue.isEmpty()) {
      Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode = queue.poll();
      //check if we reached out target node
      if (currentNode.equals(targetNode)) {
        return; // we have found our target node V.
      }
      //Add all of unvisited neighbors to the queue. We add only unvisited nodes to avoid cycles.
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
        if (!neighbor.visited) {
          visitNode(neighbor);
          neighbor.visited = true; //mark it as visited
          queue.add(neighbor);
        }
      }
    }
  }

  public static void graphBFSByLevelList(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; source) {
    //if empty graph, then return.
    if (null == source) {
      return;
    }
    Set&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; frontier = new HashSet&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //this will be useful to identify what we visited so far and also its level
    //if we dont need level, we could just use a Set or List
    HashMap&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;, Integer&amp;amp;amp;amp;amp;amp;amp;gt; level = new HashMap&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;, Integer&amp;amp;amp;amp;amp;amp;amp;gt;();
    int moves = 0;
    //add source to frontier.
    frontier.add(source);
    visitNode(source);
    level.put(source, moves);
    while (!frontier.isEmpty()) {
      Set&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; next = new HashSet&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; parent : frontier) {
        for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : parent.neighbors) {
          if (!level.containsKey(neighbor)) {
            visitNode(neighbor);
            level.put(neighbor, moves);
            next.add(neighbor);
          }
          //check if we reached out target node
          if (neighbor.equals(targetNode)) {
            return; // we have found our target node V.
          }
        }//inner for
      }//outer for
      moves++;
      frontier = next;
    }//while
  }

  public static void treeBFSByQueue(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; root) {
    //if empty graph, then return.
    if (null == root) {
      return;
    }
    Queue&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; queue = new LinkedList&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //add root to queue.
    queue.add(root);
    while (!queue.isEmpty()) {
      Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode = queue.poll();
      visitNode(currentNode);
      //check if we reached out target node
      if (currentNode.equals(targetTreeNode)) {
        return; // we have found our target node V.
      }
      //Add all of unvisited neighbors to the queue. We add only unvisited nodes to avoid cycles.
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
        queue.add(neighbor);
      }
    }
  }

  public static void treeBFSByLevelList(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; root) {
    //if empty graph, then return.
    if (null == root) {
      return;
    }
    List&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; frontier = new ArrayList&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //add root to frontier.
    frontier.add(root);
    while (!frontier.isEmpty()) {
      List&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; next = new ArrayList&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; parent : frontier) {
        visitNode(parent);
        //check if we reached out target node
        if (parent.equals(targetTreeNode)) {
          return; // we have found our target node V.
        }

        for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : parent.neighbors) {
          next.add(neighbor);
        }//inner for
      }//outer for
      frontier = next;
    }//while
  }

Depth First Search (DFS)

In this, we will search a node and all of its child nodes before proceeding to its siblings. This is similar to the maze searching. We search a full depth of the path and if we don’t find target path, we backtrack one level at time and search all other available sub paths.

Depth First Search

Depth First Search

In this,

  • Start at node S.
    • Visit A
    • Visit B
    • Backtrack to A as B don’t have any child nodes
    • Backtrack to S as A don’t have any unvisited child nodes
    • Backtrack to node S
      • Visit C
      • Visit D
      • Visit E
      • Visit F
      • Visit V – reached searching node so exit.

The path to node V from S is “S – C – D – E – F – V” and the length of it is 5. In the same graph, using BFS the path we found was “S – C – E – V” and the length of it is 3. So based on this observation, we could say DFS finds a path but it may or may not be a shorted path from source to target node.

Few of advantages of this approach are,

  • The fastest way to find a path – it may not be a shortest one.
  • If the probability of target node exists in the bottom levels. In an organization chart, if we plan to search a less experienced person (hopefully he will be in bottom layers), the DFS will find him faster compared to BFS because it explores the child nodes lot earlier compared to BFS.

DFS implementation

We can implement this in multiple ways. One is using the recursion and other is using Stack data structure. Following is the java implementation of this algorithm.

   public static void graphDFSByRecersion(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode) {
    if (null == currentNode) {
      return; // back track
    }
    visitNode(currentNode);
    currentNode.visited = true;
    //check if we reached out target node
    if (currentNode.equals(targetNode)) {
      return; // we have found our target node V.
    }
    //recursively visit all of unvisited neighbors
    for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
      if (!neighbor.visited) {
        graphDFSByRecersion(neighbor);
      }
    }
  }

  public static void graphDFSByStack(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; source) {
    //if empty graph, return
    if (null == source) {
      return; //
    }
    Stack&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; stack = new Stack&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //add source to stack
    stack.push(source);
    while (!stack.isEmpty()) {
      Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode = stack.pop();
      visitNode(currentNode);
      currentNode.visited = true;
      //check if we reached out target node
      if (currentNode.equals(targetTreeNode)) {
        return; // we have found our target node V.
      }
      //add all of unvisited nodes to stack
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
        if (!neighbor.visited) {
          stack.push(neighbor);
        }
      }
    }
  }

  public static void treeDFSByRecersion(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode) {
    if (null == currentNode) {
      return; // back track
    }
    visitNode(currentNode);
    //check if we reached out target node
    if (currentNode.equals(targetNode)) {
      return; // we have found our target node V.
    }
    //recursively visit all of unvisited neighbors
    for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
              graphDFSByRecersion(neighbor);
    }
  }

  public static void treeDFSByStack(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; source) {
    //if empty graph, return
    if (null == source) {
      return; //
    }
    Stack&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; stack = new Stack&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //add source to stack
    stack.push(source);
    while (!stack.isEmpty()) {
      Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode = stack.pop();
      visitNode(currentNode);
      //check if we reached out target node
      if (currentNode.equals(targetTreeNode)) {
        return; // we have found our target node V.
      }
      //add all of unvisited nodes to stack
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
          stack.push(neighbor);
      }
    }
  }

Following is the full sample code in Java and you can download and play with the code.

package com.sada.graph;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.Stack;

//@author Sada Kurapati &amp;amp;amp;amp;amp;amp;amp;lt;sadakurapati@gmail.com&amp;amp;amp;amp;amp;amp;amp;gt;
public class GraphTraversals {

  public static final Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; targetNode = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("V");
  public static final Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; targetTreeNode = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("D");

  public static void main(String args[]) {
    //build sample graph.
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; source = getSampleGraph();
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; root = getSampleTree();
    //graphBFSByQueue(source);
    //graphBFSByLevelList(source);
    //treeBFSByQueue(root);
    //treeBFSByLevelList(root);
    //graphDFSByRecersion(source);
    //graphDFSByRecersion(source);
    //treeDFSByRecersion(root);
    treeDFSByStack(root);

  }

  public static void graphBFSByQueue(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; source) {
    //if empty graph, then return.
    if (null == source) {
      return;
    }
    Queue&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; queue = new LinkedList&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //add source to queue.
    queue.add(source);
    visitNode(source);
    source.visited = true;
    while (!queue.isEmpty()) {
      Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode = queue.poll();
      //check if we reached out target node
      if (currentNode.equals(targetNode)) {
        return; // we have found our target node V.
      }
      //Add all of unvisited neighbors to the queue. We add only unvisited nodes to avoid cycles.
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
        if (!neighbor.visited) {
          visitNode(neighbor);
          neighbor.visited = true; //mark it as visited
          queue.add(neighbor);
        }
      }
    }
  }

  public static void graphBFSByLevelList(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; source) {
    //if empty graph, then return.
    if (null == source) {
      return;
    }
    Set&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; frontier = new HashSet&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //this will be useful to identify what we visited so far and also its level
    //if we dont need level, we could just use a Set or List
    HashMap&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;, Integer&amp;amp;amp;amp;amp;amp;amp;gt; level = new HashMap&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;, Integer&amp;amp;amp;amp;amp;amp;amp;gt;();
    int moves = 0;
    //add source to frontier.
    frontier.add(source);
    visitNode(source);
    level.put(source, moves);
    while (!frontier.isEmpty()) {
      Set&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; next = new HashSet&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; parent : frontier) {
        for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : parent.neighbors) {
          if (!level.containsKey(neighbor)) {
            visitNode(neighbor);
            level.put(neighbor, moves);
            next.add(neighbor);
          }
          //check if we reached out target node
          if (neighbor.equals(targetNode)) {
            return; // we have found our target node V.
          }
        }//inner for
      }//outer for
      moves++;
      frontier = next;
    }//while
  }

  public static void treeBFSByQueue(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; root) {
    //if empty graph, then return.
    if (null == root) {
      return;
    }
    Queue&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; queue = new LinkedList&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //add root to queue.
    queue.add(root);
    while (!queue.isEmpty()) {
      Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode = queue.poll();
      visitNode(currentNode);
      //check if we reached out target node
      if (currentNode.equals(targetTreeNode)) {
        return; // we have found our target node V.
      }
      //Add all of unvisited neighbors to the queue. We add only unvisited nodes to avoid cycles.
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
        queue.add(neighbor);
      }
    }
  }

  public static void treeBFSByLevelList(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; root) {
    //if empty graph, then return.
    if (null == root) {
      return;
    }
    List&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; frontier = new ArrayList&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //add root to frontier.
    frontier.add(root);
    while (!frontier.isEmpty()) {
      List&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; next = new ArrayList&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; parent : frontier) {
        visitNode(parent);
        //check if we reached out target node
        if (parent.equals(targetTreeNode)) {
          return; // we have found our target node V.
        }

        for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : parent.neighbors) {
          next.add(neighbor);
        }//inner for
      }//outer for
      frontier = next;
    }//while
  }

  public static void graphDFSByRecersion(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode) {
    if (null == currentNode) {
      return; // back track
    }
    visitNode(currentNode);
    currentNode.visited = true;
    //check if we reached out target node
    if (currentNode.equals(targetNode)) {
      return; // we have found our target node V.
    }
    //recursively visit all of unvisited neighbors
    for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
      if (!neighbor.visited) {
        graphDFSByRecersion(neighbor);
      }
    }
  }

  public static void graphDFSByStack(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; source) {
    //if empty graph, return
    if (null == source) {
      return; //
    }
    Stack&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; stack = new Stack&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //add source to stack
    stack.push(source);
    while (!stack.isEmpty()) {
      Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode = stack.pop();
      visitNode(currentNode);
      currentNode.visited = true;
      //check if we reached out target node
      if (currentNode.equals(targetTreeNode)) {
        return; // we have found our target node V.
      }
      //add all of unvisited nodes to stack
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
        if (!neighbor.visited) {
          stack.push(neighbor);
        }
      }
    }
  }

  public static void treeDFSByRecersion(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode) {
    if (null == currentNode) {
      return; // back track
    }
    visitNode(currentNode);
    //check if we reached out target node
    if (currentNode.equals(targetNode)) {
      return; // we have found our target node V.
    }
    //recursively visit all of unvisited neighbors
    for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
              graphDFSByRecersion(neighbor);
    }
  }

  public static void treeDFSByStack(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; source) {
    //if empty graph, return
    if (null == source) {
      return; //
    }
    Stack&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; stack = new Stack&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
    //add source to stack
    stack.push(source);
    while (!stack.isEmpty()) {
      Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; currentNode = stack.pop();
      visitNode(currentNode);
      //check if we reached out target node
      if (currentNode.equals(targetTreeNode)) {
        return; // we have found our target node V.
      }
      //add all of unvisited nodes to stack
      for (Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; neighbor : currentNode.neighbors) {
          stack.push(neighbor);
      }
    }
  }

  public static void visitNode(Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; node) {
    System.out.printf(" %s ", node.data);
  }

  public static Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; getSampleGraph() {
    //building sample graph.
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; S = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("S");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; A = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("A");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; C = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("C");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; B = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("B");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; D = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("D");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; E = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("E");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; F = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("F");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; V = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("V");

    S.neighbors.add(A);
    S.neighbors.add(C);

    A.neighbors.add(S);
    A.neighbors.add(B);

    B.neighbors.add(A);

    C.neighbors.add(S);
    C.neighbors.add(D);
    C.neighbors.add(E);

    D.neighbors.add(C);
    D.neighbors.add(E);
    D.neighbors.add(F);

    E.neighbors.add(C);
    E.neighbors.add(D);
    E.neighbors.add(F);
    E.neighbors.add(V);

    F.neighbors.add(D);
    F.neighbors.add(E);
    F.neighbors.add(V);

    V.neighbors.add(E);
    V.neighbors.add(F);

    return S;
  }

  public static Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; getSampleTree() {
    //building sample Tree.
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; A = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("A");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; B = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("B");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; C = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("C");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; D = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("D");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; E = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("E");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; F = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("F");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; G = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("G");
    Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt; H = new Node&amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;gt;("H");

    A.neighbors.add(B);
    A.neighbors.add(C);
    A.neighbors.add(D);

    B.neighbors.add(E);
    B.neighbors.add(F);
    B.neighbors.add(G);

    C.neighbors.add(H);

    return A;
  }

}

class Node&amp;amp;amp;amp;amp;amp;amp;lt;T&amp;amp;amp;amp;amp;amp;amp;gt; {

  T data;
  ArrayList&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;T&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; neighbors = null;
  boolean visited = false;

  Node(T value) {
    data = value;
    neighbors = new ArrayList&amp;amp;amp;amp;amp;amp;amp;lt;Node&amp;amp;amp;amp;amp;amp;amp;lt;T&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;();
  }

  @Override
  public int hashCode() {
    int hash = 5;
    return hash;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == null) {
      return false;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }
    final Node&amp;amp;amp;amp;amp;amp;amp;lt;?&amp;amp;amp;amp;amp;amp;amp;gt; other = (Node&amp;amp;amp;amp;amp;amp;amp;lt;?&amp;amp;amp;amp;amp;amp;amp;gt;) obj;
    if (this.data != other.data &amp;amp;amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp;amp;amp; (this.data == null || !this.data.equals(other.data))) {
      return false;
    }
    return true;
  }
}

N Queens – Backtracking algorithm

In this problem or puzzle, N queens will be given and the challenge is to place all of them in N x N chessboard so that no two queens are in attacking position. What that means is we need to find the configuration of N queens where any of the two queens do not share a row, column and diagonal paths.

One of the better and elegant solutions to this puzzle is ‘backtracking algorithm’. While we are going through the solution to the problem let us also explore the technique of backtracking.

Problem

Find all valid configuration of N Queens puzzle. To understand this better, let us look into an example – 4 queen’s problem. I am taking the 4 queens problem as there are no solutions for 2 and 3 queen’s problem and 1 queen problem is trivial one. For this, we are given 4 x 4 chessboard and we need to place 4 queens in non-attacking places on this board. Following are the only two configurations which exist for this 4 queen’s problem.

4 Queens - Solutions

4 Queens – Solutions

Backtracking Algorithm

Before we get on to the actual solution, let us look into the backtracking algorithm.  Backtracking is similar to the brute force approach where it tries all of the solutions but the only difference is that it eliminates/avoids the partial candidate solutions as soon as it finds that that path cannot lead to a solution.

To understand this further, let us look into the following tree. By the way, this is called Trie or prefix tree and let us not worry about these in this post.

Backtracking Algorithm

Backtracking Algorithm

In the above mentioned tree, what path leads to the word “BACK” (first 4 letters of backtrackingJ)?
One of the simple way, I can think is as follows.

  • Start at the root node ‘B’. Does this match with the 1st letter of the word BACK? YES
    • Then check the 1st child node A – does this match 2nd letter of the word BACK? YES
      • Then check the 1st child C – does this match 3rd letter of the word BACK? YES
        • Check the 1st child L – does this match 4th letter of the word BACK? NO
        • Check the 2nd child M – does this match 4th letter of the word BACK? NO
    • Check the 2nd child D – does this match 3rd letter of the word BACK? NO
  • Check the 2nd child node C – does this match 2nd letter of the word BACK? NO
  • Check the 3rd child node A – does this match 2nd letter of the word BACK? YES
    • Check the 1st child V – does this match 3rd letter of word BACK? NO
    • Check the 2nd child C – does this match 3rd letter of the work BACK? YES
      • Check the 1st child K – does this match 4th letter of the work BACK? YES

Following are the observations based on the above steps,

  • Every time when we hit NO, we went back to parent of that node and checked other child nodes/paths – this is back tracking
  • Avoided all of the sub paths for the node whenever we hit non match (NO). This enabled us to avoid unnecessary paths exploration(2nd node C – at level 2 and others)

Following is the generalized algorithm in pseudo code,

Take a sub problem.
       For each of the possible candidates for this sub problem
           If this can lead to the solution, then recursively
           call this for next sub problem
           If not, then skip this possibility and investigate next candidate.
If all of the sub problems are solved,
      Then we found a solution otherwise, no solution

Solution

As we now have the understanding of the backtracking algorithm let us see how that applies to our N Queens problem.

  • We can divide this problem into a sub problem of placing single queen on the chess board
  • To identify if this leads to a solution, we can check and see does any of the already placed queens attack this position. If yes, then go to next column. If no, then place this queen and move on to next queen

To illustrate this further,

  1. Did we place all of the queens?
    • YES, we found the successful configuration. Print it.
    • NO, Take the queen
  2. For each column from 1 to N
    • Does this column is safe – YES, then iterate this process for next queens – call this method recursively
    • NO – continue with next column

In Java

We can always represent the queen’s placement in an N x N array but let us use an array with N elements which stores the column positions of each array. For example, if A[2] = 5, it means the 2nd queen is placed on column 5. This way we can save the memory and also it is easy to find if a cell in the board is safe to place or not.

To find if the cell is safe of not, we need to look into two aspects.

Does any of the previously placed queen is in the same column?
This can easily be checked using the above noted array representation. Iterate through above noted array and see does any of the value matches the column we are trying to place current queen. By the way we need not check for the row as we construct the solution row by row and there will not be any queens placed in those.

Does any of the previously placed queen is in diagonal?
This is a bit tricky. The way can find is, iterate through each of the previously placed queen positions and check the absolute differences between the column and row value. If they are same, then they are in attacking position. Let us take an example here. Assume that we already placed first queen in (2, 2) – 2nd queen is placed in 2nd column. If we look at the diagonal cells (1,1), (1,3), (3,1), (3.3), we can observe that,

|r1-r2| == |c1-c2| is hold good.

  • |1-2| = |1 -2| = 1 (for cell 1,1)
  • |1-2| = |3-2| = 1 (for cell 1,3)
  • |3-2| = |1-2| = 1 (for cell 3,1)
  • |3-2| = |3-2| = 1 (for cell 3,3)

Following is the java method for the above logic,

  //check if the column is safe place to put Qi (ith Queen)
  private static boolean isSafePlace(int column, int Qi, int[] board) {

    //check for all previously placed queens
    for (int i = 0; i < Qi; i++) {
      if (board[i] == column) { // the ith Queen(previous) is in same column
        return false;
      }
      //the ith Queen is in diagonal
      //(r1, c1) - (r2, c1). if |r1-r2| == |c1-c2| then they are in diagonal
      if (Math.abs(board[i] - column) == Math.abs(i - Qi)) {
        return false;
      }
    }
    return true;
  }

Following is the java method implementation of N Queens solution using backtracking technique,

  private static void placeQueenOnBoard(int Qi, int[] board) {
    int n = board.length;
    //base case
    if (Qi == n) {// a valid configuration found.
      System.out.println(Arrays.toString(board));
    } else {
      //try to put the ith Queen (Qi) in all of the columns
      for (int column = 0; column < n; column++) {
        if (isSafePlace(column, Qi, board)) {
          board[Qi] = column;
          //then place remaining queens.
          placeQueenOnBoard(Qi + 1, board);
          /**
           * backtracking. It is not required in this as we only look previously
           * placed queens in isSafePlace method and it doesnot care what values
           * are available in next positions.*
           */
          board[Qi] = -1;
        }
      }
    }
  }

Following is the full java implementation of the above approach.

package com.sada.algorithms.backtracking;

import java.util.Arrays;
import java.util.Scanner;

/**
 *
 * @author Sada Kurapati <sadakurapati@gmail.com>
 */
public class NQueens {

  public static void main(String args[]) {
    System.out.println("How many queens? ");
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
    int[] board = new int[n]; //hold the column position of n queens
    placeQueenOnBoard(0, board);

  }

  private static void placeQueenOnBoard(int Qi, int[] board) {
    int n = board.length;
    //base case
    if (Qi == n) {// a valid configuration found.
      System.out.println(Arrays.toString(board));
    } else {
      //try to put the ith Queen (Qi) in all of the columns
      for (int column = 0; column < n; column++) {
        if (isSafePlace(column, Qi, board)) {
          board[Qi] = column;
          //then place remaining queens.
          placeQueenOnBoard(Qi + 1, board);
          /**
           * backtracking. It is not required in this as we only look previously
           * placed queens in isSafePlace method and it doesnot care what values
           * are available in next positions.*
           */
          board[Qi] = -1;
        }
      }
    }
  }

  //check if the column is safe place to put Qi (ith Queen)
  private static boolean isSafePlace(int column, int Qi, int[] board) {

    //check for all previously placed queens
    for (int i = 0; i < Qi; i++) {
      if (board[i] == column) { // the ith Queen(previous) is in same column
        return false;
      }
      //the ith Queen is in diagonal
      //(r1, c1) - (r2, c1). if |r1-r2| == |c1-c2| then they are in diagonal
      if (Math.abs(board[i] - column) == Math.abs(i - Qi)) {
        return false;
      }
    }
    return true;
  }
}

Algorithm – Knapsack

This is one of the optimization algorithm where we try lot of options and optimize (maximize or minimize based on the requirement) the output. For this algorithm, we will be given list of items, their size/weight and their values. We have a knapsack (bag pack) with a limited size/weight and we need to find the best choice of items to fit those in the knapsack so that we can maximize the value.

One of the examples where we can use this algorithm is while preparing for travelling (back packing for trekking or etc). We wish we can take everything but we will have limited backpack and need to choose the best items. One other classic example is a thief trying to rob a house or a shop. He can carry limited items and he needs to take the best choice so that he can maximize the value. (If he is a programmer, I am sure he will use this algorithm ;))

Here we will discuss only the classic flavor of knapsack problem, which is called 0-1 knapsack. It means, we can either take the full item or not. We can’t take the part of the item available.

Problem

Let us formalize the problem statement here. The input contains three parameters, price[], weights[] and  sackCapacity.

  • price[] – this array contains the prices/values of items
  • weights[] – this array holds the weights/size of the items
  • sackCapacity – this is the knapsack (backpack) capacity

So price[i] contains the price of ith item and weights[i] is the weight.

Solution

This knapsack problem can be solved with different approaches, but here we will explore the approach by an algorithm called Dynamic Programming (DP).

DP is all about breaking the original problem into sub problems, solving them and remember/store the solutions and then building the solution for original problem. In this case, we will find the best suitable or choice of items for weights from 1 to sackCapacity-1 and then we can easily build the solution for sackCapacity.

There are two different DP approaches,

  • Top down DP (Recursive) – here we start calculating solution for original problem using solutions of sub problems and where the solutions for sub problems are achieved by performing recursive calls.
  • Bottom up approach (Iterative) – here we find the solutions to smaller problems and then iteratively build the solution for original problem. For example, to calculate the Nth Fibonacci number, we start from finding 0, 1, 2 … N-1 Fibonacci numbers and then build Nth Fibonacci number. There are no recursive calls in this.

Even most of the modern computers perform poorly if Top down approach is used as it uses stack memory for recursive calls so here we will discuss the bottom up approach. As most of the DP approaches are better explained using examples rather than the algorithm itself, we will also follow the same approach to understand it better.

In simple words, we check each item and see whether we are getting the better value if we include this item in the knapsack, if yes, then take that item. If not, then skip this item.

Example

Total items – 6
Prices – {6, 4, 5, 3, 9, 7}
Weights – {4, 2, 3, 1, 6, 4}
Knapsack capacity – 10

So here all of our sub problems are – knapsack capacity with 0, 1 2, 3, 4, 5, 6, 7, 8, 9 and original problem is for knapsack capacity with 10.

As per the DP, we need to find the solutions for these sub problems and store them in a table. For this, we can take a double dimension array  – int[nItems +1][sackCapacity+1] (let us call it dpTable) where nItems is the number of items and sackCapacity is the knapsack capacity. In this, dpTable[i][w] – represents the optimal solution for i items for sack capacity weight w.

Then we need to initialize the array with 0 for first row and column as optimal solutions for 0 items with any sack capacity is zero and also for any number of items with 0 sack capacity. After the initialization, the dpTable looks like below.

Initial Knapsack DP table

Initial Knapsack DP table

Then, we perform the following steps iteratively for sack capacity from 1 to 10 (sackCapacity)

  • Check the weight of the each item, if it can’t fit (weights[i] > w) into our sack, then we move to the next weight
  • If we can fit it in (weights[i] <= w), then we calculate the optimal price by the following steps.
    • Find the price if we include this item. We just wanted to know what the price gain is if we include this in the sack. So price = price of this item + price of (sackCapacity of this sub problem – weight of this item)
    • Find the price if we don’t include this item – it will be optimal price of i-1 items.
    • Then take the best option from above – the greater price.

So the final dpTable will look like below for the above example.

Final DP Table for Knapsack

Final DP Table for Knapsack

Algorithm in Java

Following method shows the implementation of how we perform the above mentioned steps to pick the optimal items.

  /**
   * price[] - value - $$$ Gain weights[] - weights sackCapacity - the maximum
   * weight knapsack can carry.
   */
  private static boolean[][] getItemsToPick(int[] price, int[] weights, int sackCapacity) {
    int nItems = price.length;
    //dp[i][w] - the maximum value of sub problem with i items and with w sack capacity.
    //no need to initialize with zeros as in java, the defalt values are 0 for int premitive type.
    int[][] dpTable = new int[nItems + 1][sackCapacity + 1];
    boolean[][] keep = new boolean[nItems][sackCapacity + 1];

    //iterate through all of the items
    for (int i = 1; i <= nItems; i++) {
      //calculate sub problem for all weights
      for (int w = 1; w <= sackCapacity; w++) {
        if (weights[i - 1] > w) {
          // we cannt take this weight as it exceeds sub problem with weight w and i items
          dpTable[i][w] = dpTable[i - 1][w];
        } else {
          //Price if we include item i
          int pYes = price[i - 1] + dpTable[i - 1][w - weights[i - 1]];
          //Price if we include item i
          int pNo = dpTable[i - 1][w];
          if (pYes > pNo) {
            //this item MAY go into sack
            keep[i - 1][w] = true;
            dpTable[i][w] = pYes;
          } else {
            dpTable[i][w] = pNo;
          }
        }
      }
    }
    //printing dpTable
    System.out.println(Arrays.deepToString(dpTable));
    return keep;
  }

To print what items we picked, we need to keep track these choices in another boolean array. For this, we have used boolean[][] keep. Following java code backtracks the items we picked up and prints the details.

  public static void printSelectedItems(boolean[][] keep, int sackCapacity, int[] price, int[] weights) {
    //printing what items we picked
    System.out.println("Selected items:");
    int K = sackCapacity;
    int n = price.length;
    int wsel = 0;
    int vsel = 0;
    for (int i = n - 1; i >= 0; i--) { // need to go in the reverse order
      if (keep[i][K] == true) {
        System.out.println(i + "\tv=" + price[i] + "\tw=" + weights[i]);
        wsel += weights[i];
        vsel += price[i];
        K = K - weights[i];
      }
    }
    System.out.println("The overall value of selected items is " + vsel + " and weight " + wsel);
    System.out.println("Maximum weight was " + sackCapacity);
  }

Time Complexity

  • The time complexity is O(NW). Here N – is the number of items and W is the weight of the knapsack. As it is depends on number of items and also on weights, we call this pseudo polynomial. It is linear in terms of number of items but remember, W – the sack capacity can be much much bigger than N. so this is called pseudo-polynomial.
  • Space, we used O(N2) to store the DP table and same memory to keep track of what items we picked up. Actually we don’t need to have this extra keep table to backtrack items we picked up.

Following is the full java implementation of the above approach.

package com.sada.algorithms.dp;

import java.util.Arrays;

/**
 * Knapsack algorithm
 *
 * @author Sada Kurapati <sadakurapati@gmail.com>
 */
public class Knapsack {

  public static void main(String[] args) {
    int[] price = {6, 4, 5, 3, 9, 7};
    int[] weights = {4, 2, 3, 1, 6, 4};

    int sackCapacity = 10;
    boolean[][] keep = getItemsToPick(price, weights, sackCapacity);
    printSelectedItems(keep, sackCapacity, price, weights);
  }

  /**
   * price[] - value - $$$ Gain weights[] - weights sackCapacity - the maximum
   * weight knapsack can carry.
   */
  private static boolean[][] getItemsToPick(int[] price, int[] weights, int sackCapacity) {
    int nItems = price.length;
    //dp[i][w] - the maximum value of sub problem with i items and with w sack capacity.
    //no need to initialize with zeros as in java, the defalt values are 0 for int premitive type.
    int[][] dpTable = new int[nItems + 1][sackCapacity + 1];
    boolean[][] keep = new boolean[nItems][sackCapacity + 1];

    //iterate through all of the items
    for (int i = 1; i <= nItems; i++) {
      //calculate sub problem for all weights
      for (int w = 1; w <= sackCapacity; w++) {
        if (weights[i - 1] > w) {
          // we cannt take this weight as it exceeds sub problem with weight w and i items
          dpTable[i][w] = dpTable[i - 1][w];
        } else {
          //Price if we include item i
          int pYes = price[i - 1] + dpTable[i - 1][w - weights[i - 1]];
          //Price if we include item i
          int pNo = dpTable[i - 1][w];
          if (pYes > pNo) {
            //this item MAY go into sack
            keep[i - 1][w] = true;
            dpTable[i][w] = pYes;
          } else {
            dpTable[i][w] = pNo;
          }
        }
      }
    }
    //printing dpTable
    System.out.println(Arrays.deepToString(dpTable));
    return keep;
  }

  public static void printSelectedItems(boolean[][] keep, int sackCapacity, int[] price, int[] weights) {
    //printing what items we picked
    System.out.println("Selected items:");
    int K = sackCapacity;
    int n = price.length;
    int wsel = 0;
    int vsel = 0;
    for (int i = n - 1; i >= 0; i--) { // need to go in the reverse order
      if (keep[i][K] == true) {
        System.out.println(i + "\tv=" + price[i] + "\tw=" + weights[i]);
        wsel += weights[i];
        vsel += price[i];
        K = K - weights[i];
      }
    }
    System.out.println("The overall value of selected items is " + vsel + " and weight " + wsel);
    System.out.println("Maximum weight was " + sackCapacity);
  }
}
Image

Algorithm – Tower of Hanoi Game with K Towers

The standard Tower of Hanoi game is with 3 towers and we need to move all discs from one tower to another. This post provides a flavor of same puzzle but with more than 3 towers and given an initial configuration, we need to find the minimum moves required to go to a target configuration.

Problem

We will be given the discs with radius 1 to N, towers (K), an initial configuration and target configuration. We need to calculate the minimum number of moves we need to make to get to the target configuration starting from initial configuration.

Few rules:

  • N discs – there are N discs each with radius of 1 to N
  • K towers
  • Disc with radius R can be added to the tower if and only if it is empty or the top disc’s radius is < R
  • 1<= N<=8 and 3<= K<=5 – these constraints help us in building a more practical algorithm
  • Read the input from STDIN – System.in and print the output to STDOUT – System.out

Input format

  • First line consists of two integers N and K with space delimited
  • 2nd line consists of initial configuration. It is provided as space delimited integers where index is the disc radius and the number at that index is the tower it contains
  • 3rd line consists of target or desired configuration. It is provided in the same initial configuration.

Sample Input

6 4
4 2 4 3 1 1
1 1 1 1 1 1
Here,

  • N=6 (discs) and K=4 (towers)
  • Initial configuration is “4 2 4 3 1 1”
    • Tower 1 have 6 and 5 discs
    • Tower 2 have disc 2
    • Tower 3 have disc 4
    • tower 4 have 3 and 1 discs
  • Target configuration is “1 1 1 1 1 1”
    •  all discs from 6 to 1 are on tower 1

Following diagram depicts the source and target Tower of Hanoi configurations. Note that the discs are not according to the radius, but they are color coded to represent appropriate radius for simplicity.

tower_of_hanoi

Output format

  • 1st line should be the minimum number of moves required to get to target configuration from initial configuration
  • From 2nd line, print from and to tower for a given move. “4 3”  – means a disc is moved from 4th tower to 3rd tower

Sample output

Following is the output for above given sample input.
5
3 1
4 3
4 1
2 1
3 1
Here,

  • 5 – minimum number of move to get to target configuration
  • Then each line consists of from and to towers for each of those 5 moves. For example, “3 1” represents a disc is moved from tower 3 to 1

Solution

There are different ways to solve this problem. Here we will discuss a simple and object oriented approach. We can model this solution as follows.

  • This can be solved using a Breadth-First-Search (BFS). Why BFS? It is the best way to travel a level by level and find the shortest path from a given node to target node
  • Consider the initial configuration as the starting node
  • Then get all of its neighboring nodes – all possible legal configuration we can get from current configuration and repeat this step until we reach target configuration or the graph is exhausts
  • Towers can be easily represent as a Stack
  • Node contains K towers – it represents a configuration

In Java

Following is the full java implementation of the above approach. Please leave your feedback in comments section.

package com.sada.puzzles;

import com.sada.puzzles.TOH.Node.Move;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;

/**
 *
 * @author Sada Kurapati <sadakurapati@gmail.com>
 */
public class TOH{

  public static void main(String args[]) {
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
    int k = sc.nextInt();
    //towers initial config
    Node source = readPegsConfiguration(k, n, sc);
    //read target configuration
    Node target = readPegsConfiguration(k, n, sc);
    //To keep track what config we visited and avoid cycles
    Set visited = new HashSet();
    try {
      minMovesToTarget(source, target, visited);
    } catch (Exception ex) {
      System.out.println("Exception = " + ex);
    }
  }

  private static void minMovesToTarget(Node source, Node target, Set visited) throws CloneNotSupportedException {
    //Perform BFS
    //add source node to Queue
    Queue q = new LinkedList();
    q.add(source);
    Node current = source;
    while (!q.isEmpty()) {
      current = q.poll();
      if (current.equals(target)) { //found the target configuration
        break;
      }
      List neighbors = current.neighbors();
      if (neighbors.size() > 0) {
        for (Node n : neighbors) {
          if (!visited.contains(n)) {//if not visited, put it in queue
            q.offer(n);
            visited.add(n);
          }
        }
      }
    }
    //Printing path and moves if target config found
    if (current.equals(target)) {
      printOutput(current);
    }
  }

  private static Node readPegsConfiguration(int k, int n, Scanner sc) {
    Stack[] initialState = new Stack[k];
    for (int i = 0; i < k; i++) {
      initialState[i] = new Stack();
    }
    //reading and reversing the line as we need to put the elements in decresing size
    //disc is key and tower is value.
    TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>(Collections.reverseOrder());
    for (int i = 0; i < n; i++) {
      map.put(i, sc.nextInt());
    }
    //prepare towers
    for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
      initialState[entry.getValue() - 1].push(entry.getKey());
    }
    return new Node(initialState);
  }

  static void printOutput(Node target) {
    Stack stack = new Stack<>(); //using stack as we need to print the trail from Source - target config
    while (target.parent != null) {
      stack.add(target.move);
      target = target.parent;
    }
    System.out.println(stack.size());
    while (!stack.isEmpty()) {
      System.out.println(stack.pop());
    }
  }

  static class Node implements Cloneable {
    //towers
    Stack[] state = null;
    Node parent = null;  //for backtracking trail
    Move move = null; // The move we made to go to next neighbor config

    public Node(Stack[] st) {
      state = st;
    }

    @Override
    protected Node clone() throws CloneNotSupportedException {
      Stack[] cloneStacks = new Stack[state.length];
      for (int i = 0; i < state.length; i++) {
        cloneStacks[i] = (Stack) state[i].clone();
      }
      Node clone = new Node(cloneStacks);
      return clone;
    }

    //returns the neghboring configurations.
    //What all configurations we can get based on current config.
    public List neighbors() throws CloneNotSupportedException {
      List neighbors = new ArrayList<>();
      int k = state.length;
      for (int i = 0; i < k; i++) {
        for (int j = 0; j < k; j++) {
          if (i != j && !state[i].isEmpty()) {
            //Need to clone to avoid change the parent node.
            //Hint - in java, objects are not mutable and they are references
            Node child = this.clone();
            //make a move
            if (canWeMove(child.state[i], child.state[j])) {
              child.state[j].push(child.state[i].pop());
              //this is required to backtrack the trail once we find the target config
              child.parent = this;
              //the move we made to get to this neighbor
              child.move = new Move(i, j);
              neighbors.add(child);
            }
          }
        }
      }
      return neighbors;
    }

    public boolean canWeMove(Stack fromTower, Stack toTower) {
      boolean answer = false;
      if (toTower.isEmpty()) {// if destination tower is empty, then we can move any disc
        return true;
      }
      int toDisc = toTower.peek();
      int fromDisc = fromTower.peek();
      if (fromDisc < toDisc) { //we can only place small disc on top
        answer = true;
      }
      return answer;
    }

    @Override
    public int hashCode() {
      int hash = 7;
      return hash;
    }

    @Override
    public boolean equals(Object obj) {
      if (obj == null) {
        return false;
      }
      if (getClass() != obj.getClass()) {
        return false;
      }
      final Node other = (Node) obj;
      if (!Arrays.deepEquals(this.state, other.state)) {
        return false;
      }
      return true;
    }

    class Move {
      int towerFrom, towerTo;
      public Move(int f, int t) {
        towerFrom = f + 1;
        towerTo = t + 1;
      }
     @Override
      public String toString() {
        return towerFrom + " " + towerTo;
      }
    }
  }
}

Quicksort – a practical and efficient sorting algorithm

Today let us look into another sorting algorithm. This is one of the popular and most efficient for majority of the practical usages. It has worst case time complexity of O(n2) but performs lot better in average case with time complexity of O(n log n). This performs better than MergeSort and HeapSort which also have same asymptotic time complexity O(n log n) on average case but the constant factors hidden in the asymptotic time complexity for quick sort are pretty small.

Overview

Like merge sorting, this follows the same Divide and Conquer paradigm. Following are the details on three steps of divide and conquer paradigm and how Quicksort performs the sorting of an array A[i…j] (i & j are indexes and i < j).

  • Divide: divide (partition) the array into two sub arrays A[i…k-1] and A[k+1…j] (j < k < j), such that all of the elements in A[i…k-1] are less than are equal to A[k] and all of the elements are in A[k+1..j] are greater than A[k]. A[i..k-1] <= A[k] < A[k+1…j]
  • Conquer: Sort the sub arrays A[i..k-1] and A[k+1…j] by calling quick sort recursively
  • Combine: As the sub arrays are sorted, no need to perform any work in this step. The array A[i…j] is sorted

So on high level,

  1. Pick an element – typically this is called pivot.
  2. Move all elements smaller than equal to picked element to left
  3. Move all elements greater then picked element to right
  4. Perform steps 1 to 3 recursively on left and right sub arrays till we have array with one element were the divide bottoms out.

For now let us look at the coding aspects of the standard single pivot quick sort.

Algorithm

Following is the pseudo code for the quicksort algorithm.

 QUICKSORT(A, p, r)
   If p < r
     q = PARTITION (A, p, r)
     QUICKSORT(A, p, q-1)
     QUICKSORT(A, q+1, r)

In Java,

  public static void quickSort(int[] a, int p, int r) {
    if (p < r) { // there are at least two elements in the array
      int q = partition(a, p, r);
      quickSort(a, p, q - 1);
      quickSort(a, q + 1, r);
    }
  }

To sort the array, the initial call would be QUICKSORT(A, 0, A.length-1) – for zero index based arrays. So if you look at the above algorithm it is clear that the meat of the logic goes into the partition mechanism.

Following is the partition logic. – Note that there are bunch of variations and we are discussing here the standard partition algorithm.

 PARTITION(A, p, r)
   pivot = A[r]  - picking the last element as the pivot.
   i  = p -1
   for j = p to r-1
     if A[j] <= pivot
     i = i+1
     swap A[i] and A[j]
   swap A[i+1] with A[r] – this will put the pivot in right place
 return i+1

In Java,

  //performs the partition using last element
  public static int partition(int[] a, int p, int r) {
    //choosing last element in the array as pivot
    int pivot = a[r];
    int i = p - 1;
    int temp;
    for (int j = p; j < r; j++) {
      if (a[j] <= pivot) {
        i++;
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
      }
    }
    //now place pivot in right place
    temp = a[i + 1];
    a[i + 1] = a[r];
    a[r] = temp;
    return (i + 1);
  }
}

Loop Invariant

A loop invariant is something which is true in following three stages.

  1. Before loop execution begins – Initialization
  2. While loop is executing – Maintenance
  3. After the loop exits – Termination

Following is pictorial representation of quick sort loop invariant

Quicksort loop invarient

So the loop invariant for quick sort is

  • All elements in A[p…i] are less than or equal to pivot
  • All elements in A[i+1…j-1] are greater than pivot
  • A[r] is the pivot element

Example

Let us look at an example and some pictorial representation to understand the sorting and loop invariant clearly.  The following diagram just depicts the first pass of partitioning.

Quicksort partition

So once the above partition is done, then quick sort is called recursively on the left and right sub arrays to pivot element. Then the same partition is performed recursively till we have sub arrays with one element.

Variations

By the way, there are bunch of quick sort flavors based on the mechanism used to pick an element (pivot) to partition the array. Following are few of them,

  • Pick single element as pivot – First, last, middle or some randomly located element (A[i], A[j] or A[k] where k=(i+j)/2 or A[p] –  p is random index)
  • Pick two or multiple elements as pivots – pick first and last or any two elements as pivot. There are different variations in this category – 3-way partition, Dual Pivot quick sort (by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch), etc.

Summary

Although the worst case time complexity of this sorting algorithm is quadratic, it performs very efficiently in average case.

  • The worst case time complexity is O(n2) and average case is O (n log n)
  • This is very efficient and more often the best practical algorithm
  • Bunch of the flavors are available and can easily customize or choose best suitable partition algorithm based on probable data sets
  • The standard algorithm is not stable. If stability is required, we might need to use extra space
/*
 * The MIT License
 *
 * Copyright 2013 Sada Kurapati <sadakurapati@gmail.com>.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.sada.algorithms;

import java.util.Arrays;

/**
 *
 * @author Sada Kurapati <sadakurapati@gmail.com>
 */
public class QuickSort {

  private static int QSortSwaps = 0;

  public static void main(String args[]) {
    int[] input = new int[]{1, 3, 9, 8, 2, 7, 5};
    System.out.printf("Before sorting: %s \n", Arrays.toString(input));
    quickSort(input, 0, input.length - 1);
    System.out.printf("After sorting: %s \n", Arrays.toString(input));
    System.out.println("Number of swaps: " + QSortSwaps);
  }

  public static void quickSort(int[] a, int p, int r) {
    if (p < r) { // there are at least two elements in the array
      int q = partition(a, p, r);
      quickSort(a, p, q - 1);
      quickSort(a, q + 1, r);
    }
  }

  //performs the partition using last element
  public static int partition(int[] a, int p, int r) {
    //choosing last element in the array as pivot
    int pivot = a[r];
    int i = p - 1;
    int temp;
    for (int j = p; j < r; j++) {
      if (a[j] <= pivot) {
        i++;
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
        ++QSortSwaps;
      }
    }
    //now place pivot in right place
    temp = a[i + 1];
    a[i + 1] = a[r];
    a[r] = temp;
    ++QSortSwaps;
    return (i + 1);
  }
}

Insertion Sort – Efficient way of counting swaps

Problem Statement

Insertion Sort is a simple sorting technique which was covered in my previous blog post Algorithms – Insertion Sort .  On a high level, the sorting will pick an element (iterated the process from 2nd element to last element), shifts all elements which are greater than the picked up element in the left part of the array by one position to the right and inserts it at correct position.

In this post, let’s look at a special aspect of Insertion sorting which is counting how many shifts (or swaps) it takes for an insertion sort to finish sorting an array. So our challenge is how can we calculate or find out how many swaps are required for a given array efficiently.

Note that performance is the key element here.

Solutions

To find this, first we need to have a good understanding of Insertion and merge sorting algorithms. Following posts provide a very good details about these algorithm techniques.

Approach 1 – Count swaps while performing Insertion sort

Perform the insertion sort and count every element swaps. Following is the code for this in Java.

  public static int getElementSwaps(int[] ar, int size) {
    //base case
    if (size <= 1) {
      return 0;
    }
    int shifts = 0;
    //starting at 2nd element as first element is already sorted.
    //Loop Invariant - left part of the array is already sorted.
    for (int i = 1; i < size; i++) {
      int moveMe = ar[i];
      int j = i;
      while (j > 0 && moveMe < ar[j - 1]) {
        //Move element
        ar[j] = ar[j - 1];
        --j;
        //increase the count as element swap is happend
        ++shifts;
      }
      ar[j] = moveMe;
    }
    return shifts;
  }

How much time will it take? O(N2).

The performance of this is acceptable for small input sizes and will start degrading for larger input sizes and becomes quickly unacceptable. Try to test this algorithm for input sizes with greater than a million elements.

How to make it better? Let’s look at the problem statement again. We are interested in finding the swaps and not sorting the array (Hint: moving elements in array will take some time). So let us look at another option where we can avoid moving elements and just count the swaps.

Approach 2 – Finding bigger elements

Look at the insertion sorting algorithm closely and can you figure out when exactly an element swap is happening? The elements are swapped whenever a bigger element is found on left side of sorted array while trying to insert picked up element in correct position. So we can use this technique and count the number of swaps required without moving the elements in the array.

Following is the Java code for this technique.

  public static int getBiggerElementsCount(int[] ar, int size) {
    //base case
    if (size <= 1) {
      return 0;
    }
    int biggerElements = 0;
    //starting at 2nd element as first element is already sorted
    //and no swaps are required.
    for (int i = 1; i < size; i++) {
      //find number of elements less than current
      int moveMe = ar[i];
      for (int j = i - 1; j >= 0; j--) {
        if (moveMe < ar[j]) {
          //found small element on my (ar[i]) left
          ++biggerElements;
        }
      }
    }
    return biggerElements;
  }

What is the time complexity of this? Asymptotically, it is still O(N2) but it will perform much better compared to approach 1 as we tried out best to improve the constant factors in this asymptotic time complexity. Is there a better algorithm for this problem?

Approach 3 – Inversions and Merge Sort

An inversion is nothing but a situation where we need to invert (swap) the elements to make the array sorted. So in technical terminology, in an array A[1 .. n] there is a pair (i, j) such that i < j (<=n) and A[i] > A[j]. So to figure out how many swaps are required to sort using insertion sorting algorithm, we need to find our number of inversions required to make the array sorted.

One of the best approaches for this is to modify the merge sort algorithm so that we can count the inversions in that array while performing the sorting. For more details on merge sort, please check merge sort post

If you can recall, the merge sort divides the array recursively till it reaches array with single element (base case, array with 1 element is sorted) and then merges left and right sorted arrays using a merge routine. So how do we count the inversions in merge routine? For each element in left array, we would like to know how many elements in right are smaller and this will give us the required inversions. To this, we need to look into the two cases where this can happen in merge routine.

  • Whenever we put an element from left array, we need to increment the inversions with the count of elements [j] which are already added to result/merged array
  • Once one of the array is completely merged/exhausted(after merge loop), if there are any elements left in left array, then we need to increment the count of inversions with (number of elements already merged from right array * elements left out in left array)

Let us take an example here to understand this.

left[] = {5, 8, 10} and right[] = {2, 4, 6}

Merge routine steps

  • Inversions = 0;
  • Check first element from left (5) and right (2), insert 2 into result array – result[] = {2}
  • Check first element from left (5) and right (4), insert 4 into result array – result[] = {2, 4}
  • Check first element from left (5) and right (6), insert 5 into result array – result[] = {2, 4, 5}
    • Here, we are inserting left element, so we need to increment the inversions with number of elements from right array already placed in result. So the inversions += 2 (as element 2 and 4 are already placed in result array)
    • Check first element from left (8) and right (6), insert 6 into result array – result[] = {2, 4, 5, 6}
    • Place remaining elements from left to result array – result[] = {2, 4, 5, 6, 8, 10}
      • Inversions += (number elements left in left array * elements already placed in result from right array )
      • Inversions+= 2 * 3.

So the total number of inversions or swaps required for sorting input[] = {5, 8, 10, 2, 4, 6}  array are 8.

As we are programmers, you can execute the following code and validate this swaps count.

  public static void getInversions(int[] nums, int left, int right) {
    if (left < right) {
      //Split in half
      int mid = (left + right) / 2;
      //Sort recursively.
      getInversions(nums, left, mid);
      getInversions(nums, mid + 1, right);
      //Merge the two sorted sub arrays.
      merge(nums, left, mid, right);
    }
  }

  private static void merge(int[] nums, int left, int mid, int right) {
    int leftLength = mid - left + 1;
    int rightLength = right - mid;
    int[] lAr = new int[leftLength];
    //Just for simplicity, we are creating this right array.
    //We could use actual nums array with mid and right indexes.
    //a place to improve memory foot print.
    int[] rAr = new int[rightLength];
    for (int i = 0; i < leftLength; i++) {
      lAr[i] = nums[left + i];
    }
    for (int i = 0; i < rightLength; i++) {
      rAr[i] = nums[mid + 1 + i];
    }
    int i = 0, j = 0, k = left;
    while (i < leftLength && j < rightLength) {
      if (lAr[i] <= rAr[j]) {
        nums[k] = lAr[i];
        inversions += j;
        i++;
      } else {
        nums[k] = rAr[j];
        j++;
      }
      k++;
    }
    //remaining iversions, using long cast as multiplication will be out of
    //Integer range for large inputs
    inversions += (long) j * (leftLength - i);
    if (i >= leftLength) {
      //copy remaining elements from right
      for (; j < rightLength; j++, k++) {
        nums[k] = rAr[j];
      }
    } else {
      //copy remaining elements from left
      for (; i < leftLength; i++, k++) {
        nums[k] = lAr[i];
      }
    }
  }

By the way, there is another elegant way to do this same using data structure Binary Indexed Tree. I am not familiar with that DS as of now and will circle back to this once I learn BIT data structure.

package com.sada.algorithms;

/**
 *
 * @author Sada Kurapati(sadakurapati@gmail.com)
 */
public class InsertionSortingElementShifts {

  //Using long so that this program can be used to count inversions
  //for the input size more than 100,000 and for that, the inversions
  //are out of Integer range.
  private static long inversions = 0l;

  public static void main(String[] args) {
      int[] ar = {1,4,7,2,4};
      getInversions(ar, 0, ar.length - 1);
      System.out.println(inversions);
      //System.out.println(getBiggerElementsCount(ar, ar.length));
      //System.out.println(getElementSwaps(ar, ar.length));
  }

  //I sort the array using merge sort technique.
  public static void getInversions(int[] nums, int left, int right) {
    if (left < right) {
      //Split in half
      int mid = (left + right) / 2;
      //Sort recursively.
      getInversions(nums, left, mid);
      getInversions(nums, mid + 1, right);
      //Merge the two sorted sub arrays.
      merge(nums, left, mid, right);
    }
  }

  private static void merge(int[] nums, int left, int mid, int right) {
    int leftLength = mid - left + 1;
    int rightLength = right - mid;
    int[] lAr = new int[leftLength];
    //Just for simplicity, we are creating this right array.
    //We could use actual nums array with mid and right indexes.
    //a place to improve memory foot print.
    int[] rAr = new int[rightLength];
    for (int i = 0; i < leftLength; i++) {
      lAr[i] = nums[left + i];
    }
    for (int i = 0; i < rightLength; i++) {
      rAr[i] = nums[mid + 1 + i];
    }
    int i = 0, j = 0, k = left;
    while (i < leftLength && j < rightLength) {
      if (lAr[i] <= rAr[j]) {
        nums[k] = lAr[i];
        inversions += j;
        i++;
      } else {
        nums[k] = rAr[j];
        j++;
      }
      k++;
    }
    //remaining iversions, using long cast as multiplication will be out of
    //Integer range for large inputs
    inversions += (long) j * (leftLength - i);
    if (i >= leftLength) {
      //copy remaining elements from right
      for (; j < rightLength; j++, k++) {
        nums[k] = rAr[j];
      }
    } else {
      //copy remaining elements from left
      for (; i < leftLength; i++, k++) {
        nums[k] = lAr[i];
      }
    }
  }

  public static int getBiggerElementsCount(int[] ar, int size) {
    //base case
    if (size <= 1) {
      return 0;
    }
    int biggerElements = 0;
    //starting at 2nd element as first element is already sorted
    //and no swaps are required.
    for (int i = 1; i < size; i++) {
      //find number of elements less than current
      int moveMe = ar[i];
      for (int j = i - 1; j >= 0; j--) {
        if (moveMe < ar[j]) {
          //found small element on my (ar[i]) left
          ++biggerElements;
        }
      }
    }
    return biggerElements;
  }

  public static int getElementSwaps(int[] ar, int size) {
    //base case
    if (size <= 1) {
      return 0;
    }
    int shifts = 0;
    //starting at 2nd element as first element is already sorted.
    //Loop Invarient - left part of the array is already sorted.
    for (int i = 1; i < size; i++) {
      int moveMe = ar[i];
      int j = i;
      while (j > 0 && moveMe < ar[j - 1]) {
        //Move element
        ar[j] = ar[j - 1];
        --j;
        //increase the count as element swap is happend
        ++shifts;
      }
      ar[j] = moveMe;
    }
    return shifts;
  }
}

Hashing – a programmer perspective

As a programmer, I would say this is one of the best and most important techniques which I came across so far.

What is hashing?

To understand this, let us look at some of the definitions from the dictionary.

  • Hash (noun) – a dish of diced or chopped vegetables, a jumble
  • Hash (verb) – to chop into small pieces; make into hash
  • Hashing (Computers) – a technique for locating data in a file by applying a transformation, usually arithmetic, to a key

In simple words, it is a technique to transform a bigger data into a small key (word or number) and then using this key to identify that data whenever it is required. Let us take an example to understand this.

A simple example is the phone book. When we want to call a person named Sada, what exactly we do, go to phone book click on “S” and then search for contact called Sada. Here the transformed key (chopping and taking first letter) is simply the first letter of the contact.

Little complex example is finding a duplicate web page over the internet. As per Google, there are more than 60 trillion pages and if we take a page and compare the full content with all other pages, then it might take months or even years for us to find a duplicate page. To simplify this, we can take the first letter of each paragraph and create a key (for this blog page it will be ATIAL so far). So we simply compare the key of each web page, whenever it matches, then we compare the full content of the page and see if it is a duplicate. Hope now you can understand the power of hashing.

How it is used in programming?

To understand the advantages of this, first let us look at Array and List data structures and see the performance of most important and common operations on these in programming.

  • Adding/Inserting an element – this will take constant [O(1)] time as we can simply add at the end
  • Searching an element – this will take linear time [O(n)] as we need to compare each element from start to end in worst case.  If elements are ordered, then we can use binary search and find it in logarithmic time lg(n)
  • Removing/Deleting an element – to delete, we need to find it which again, takes linear or logarithmic time depending on the ordered or not

How do hashing based data structures perform?

  • Adding – in constant time [O(1)]
  • Finding – in constant time [O(1)]
  • Removing – in constant time [O(1)]

By the way, these are amortized time complexities and it is nothing but the average.

Amortized time =  (The Sum of time taken by N operations) / (Number of operations(N))

Don’t you think the constant time for all of the operations is really amazing?  Let us see how we can achieve this. Following are two basic principles behind these data structures.

  • Hash the data using a best suited hashing function and create a key which should be a number
  • Use an array (or tree) and store the data at that index. Sometimes this index or slot is also referred to as bucket. Why buckets? In some scenarios, we might end up storing (Chaining) more than one element at same index (slot)
  • Choose a hashing algorithm which spreads out the records as uniformly as possible over the range of addresses available

Let us understand the above by looking at the basic concepts and how we will perform them on an example to store some random numbers.

Direct Address Tables

In this, we simply store the elements directly at the position of the input values. For input 13, we store it at index 13 (Array[13]). We can represent this as hash function h(n) = n, where h is hashing function and n is input data.

Hash function – do nothing, just store the number at that index

Input data is stored internally with direct addressing hashing function

Figure 1 – This diagram depicts the details on how input data is stored internally with direct addressing hashing function

Issues

  • What should be the size of an array?
  • If we just store 31, 19, 18, 27, 13, 356, 634, 925, 92 and 10,000, then the array size will be 10,000
  • Only 10 slots are used and remaining 9,990 spaces (memory) is not used and wasted

The Division Method

Hash function: key = n % m where n is a number to be inserted and m is the size of an array – For example create an array with size 10, divide the number with 10 and take the reminder and store the number at that index. For number 634, the remainder is 4 (634 % 10). So store this number at 4th position.

Input data is stored internally with hashing function h(n) = n % m, where n is the input data/key and m=10

Figure 2 – This diagram depicts the details on how input data is stored internally with hashing function h(n) = n % m, where n is the input data/key and m=10

Advantages

  • We can store all of the numbers 31, 19, 18, 27, 13, 356, 634, 925, 92 and 10,000 in very small array with just size 10 as the reminders of the above number are 1, 9, 8, 7, 3, 6, 4, 5, 2, 0
  • Very little memory is wasted
  • Simple to extend when more elements are added. If 11th element is added, then we double the array size to 20. This size increasing is called doubling as we doubled the size. There are many other sophisticated mechanisms and we can create our own if required

Issues

  • What happens when the reminder hash function generates same key for some different elements? Ex: the hash function for numbers 13, 723 and 53 will be same key 3. This is called collision

Hash Collisions

If a hash function generates same key for two or more distinct elements, then it is called hash collision. This is practically unavoidable even with best and perfect hashing functions. Following are few workarounds to resolve this collision.

  • Chaining
  • Open Addressing
    • Probing (linear, quadratic)
    • Double hashing

Resolving Collisions by Chaining

  • Rather than storing the elements directly in internal array, store the link to a linked list
  • When the hash function generates the same hash key for two are more different elements, then insert the elements in linked list

hashing function h(n) = n % m, where n is the input data/key and m=10

Figure 3 – This diagram depicts the details on how input data is stored internally with hashing function h(n) = n % m, where n is the input data/key and m=10

Issues with Chaining

One of the issues with this chaining approach is if the hash function is not properly designed, and then there might be chance of generating the same key to lot of elements. For example, in our hash function h(n) = n % m and m= 10, what happens if we insert 1,21,31,41,51,61,71,81 and 91?

All of these elements will have same key 3 and these should be stored in linked list at index 3. In this case, again the basic operations (search, insert, remove) time cost will become linear O(n).

hashing function h(n) = n % m, where n is the input data 1, 21, 31, 41, 51, 61, 71, 81 and 91 and m=10

Figure 4 – This diagram depicts the details on how input data is stored internally with hashing function h(n) = n % m, where n is the input data 1, 21, 31, 41, 51, 61, 71, 81 and 91 and m=10

How can we resolve this? We have multiple ways here –

  • Increasing the size of array. Say m=30, then the elements will be store at 1,21,1,11,21,1,11,21 and 1. This will not completely solve the issue and we end up wasting more space
  • Use different hashing function which can distribute the elements across the array indexes. Something like h(n) = (n%11) % m. Why 11? Typically using a prime number (which is near the array size m) in modulo hashing functions will distribute the elements across the array. In this case, the elements will be stored at 1, 0, 9, 8, 7, 6, 5, 4 and 3. See how equally they are distributed now
  • Use probing technique

Hashing - Figure 5

Figure 5 – This diagram depicts the details on how input data is stored internally with hashing function h(n) = (n%11) % m, where n is the input data 1, 21, 31, 41, 51, 61, 71, 81 and 91 and m=10

Resolving Collisions by Probing

In this elements are stored in the array itself. When two are more elements are hashed to the same key, then we simply look for the next empty or available slot in the array from hashed slot. For example, if an element 13 is hashed to key 3 (n%m), then we check if the slot 3 is empty, if it empty then we will insert there and we are done. If it is not empty, we will look for next slot 4 (assuming the linear probing interval is 1) and continue this till we find any empty slot and insert this element.

  • Linear probing – In this, the next probe location is calculated based on a fixed number. For example, if the probe interval is decided as 2 and the hash key is 3, we first check at 3, if it is not empty, then we will check at 5, 7, 9 (key + probe interval) and will continue till we insert the element
  • Double hashing – In this, the next probe location is calculated by another hash function

Summary

  • This is most important and useful technique
  • Hashing is a technique for locating data in a file by applying a transformation, usually arithmetic, to a key
  • Choose the appropriate hashing algorithm so that the elements/records will spread out as uniformly as possible over the range of addresses available

Random Programming rants

Following are some of the random programming rants which I wanted to share today.

Topics

  • Language constructs and usages.
  • Time and memory footprint of our code.
  • Data structures and algorithms in Java

Language constructs and usages:

Have you ever wondered what exactly happens or how many machine level instructions are performed when we write an IF or any conditional statement (Hint: Machine only understands 1 or 0)? How do variables and data is stored in Heap or Stack when a program is executed and when they will be removed? Think for some time and Google it. For now, let us concentrate on few of the language constructs which can affect our code’s time and memory footprint big time.

Loops (Java: for, while)

I have seen many times people writing loops (including myself) without thinking anything about the size of data we are going to iterate and how many times it is going to execute.  I would recommend asking following questions and think through before writing any loops in our code.

  • What is the size or how many times the loop code will be executed?
  • Do we really need a loop or can we represent the array/list data in another data structure which can avoid all these iterations? I have seen many cases where list or array has been used where dictionary ADT (Java: Hashmap or Hashtable) could have served better, because we were iterating through all of the objects every time to find an object in the list. Another scenario is trying to find the number of particular type of object, in this case either maintain an extra variable for size, keep it consistent or can always use multiple lists for each type.
  • Always avoid the nesting of loops and nesting them to 3 levels is almost a crime in programming world. If we have a list with 100(n, linear) elements, for two level nested loops the instructions going to be executed 100,00(n<sup>2</sup>, quadratic) and for 3 nested loops, it is 1,000,000 (n<sup>3</sup>, cubit) times. So always try your best to keep things to execute in linear time.
  • Do you know binary search algorithm? That concept can be used in some of the cases and time complexity can be reduced from linear (n) to logarithmic (log n).
  • Also look into Divide and Conquer algorithm paradigm; it can also reduce the time complexity from linear (n) to logarithmic (log n).

So in simple words, ALWAYS be aware of the number of times the code in the loop is going to execute and avoid as much iterations as you can.

Recursion

This is one of the powerful concepts which can simplify our code a lot and also can impact the memory big time. We need to be very careful about the variables or data we are storing in every recursion call to subroutine because, it will keep all of the variables or data alive until all of its subsequent (subroutine) calls are completed. There are two kinds of recursions.

  • Standard recursion – On most of the current computer architectures, this implementation performs very poorly. So avoid if you can or use tail recursion if possible.

Tail recursion – in this we pass the calculated details to next recursive call rather than waiting till all of the subsequent recursive calls are completed.

Time and memory footprint

Always be aware of the time and memory footprint of every code block we are adding in. Also it is really important to understand the internals of all data structure (ADT) implementations in the programming language.

List (Java: ArrayList)

– a dynamically growing array

Have you ever wondered how can this grow dynamically and how are objects are stored inside.

  • It is backed up by any array to store the data.
  • When the array is full and we try to add, then it will create a new array with bigger size and copies all elements from old array to newly created array.
  • When the elements are removed, all of the subsequent elements are shifted forward and if the size is reduced to certain point, then the length of inside array will be reduced /re-sized accordingly.

So it means sometimes adding an element to list will take almost linear time as it needs to iterate through existing elements and copy one by one. There are other mechanisms where we can achieve better time complexity though. Also when the element is removed (from middle) it can also take linear time.

Dictionary

(Java: HashMap, HashTable etc):

As you might be aware, this will store key value pairs and it is very useful whenever we want to store and retrieve the data using a key. One of the best implementation of this is using hashing. Following are high level details.

  • It is backed up by an array to store the data
  • Every key is converted to integer which will be treated as index in internal array and corresponding value will be stored in inside array at that index
  • What happens if both keys are derived to same number? There are two ways to resolve this, chaining and probing. Google them and read about these concepts. It is very important to know about these and will help you in understanding the internal implementations
  • It provides (amortized) constant time O(1) for add, remove and find operation

Few other general rules which we need to keep in mind to reduce the time and memory footprints are,

  • Always retrieve what is needed or requested by user. For example, there no need to retrieve 1000 records when user can see only 50 at a time
  • Think of doing lazy loading wherever possible.

Avoid frequent calculations  – if possible store the values and reuse (Hint: Dynamic Programming)

Data structures and algorithms in Java

List implementations (ArrayList)

  • If we don’t specify the initial size, then it is created with size 10. If we know or can guess the size, provide that in advance so that we can avoid creating internal array multiple times.

When huge objects are stored in the list, try to utilize trimToSize() method whenever possible to minimize the memory storage.

Sorting

  • We always use Collections.sort or Arrays.sort for sorting list or arrays and did you ever wonder what sorting algorithms are used inside?
  • Collections.sort first converts the List to array and then calls Arrays.sort
  • For all primitives (byte, char, int, long etc): A tuned quick sort algorithm is used and for all objects sorting it uses the tuned merge sort algorithm

In java version 7, the objects sorting algorithm is changed to Dual-Pivot Quicksort from modified Quick sort which leads to better performance.

HashMap, Hashtable, IdentityHashMap

  • IdentityHashMap uses linear probing for resolving collisions and all others use chaining
  • IdentityHashMap uses == (reference equality) when comparing the keys and where as other implementations use Object.equals (object equality) method
  • Object.hashCode() API documentation
    • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application
    • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result

It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results.

Tree implementations (TreeMap & TreeSet)

  • All of the keys are stored in sorted order
  • They use a balanced binary tree called Red-Black tree
  • All of the operations (add, remove and find) are performed in logarithmic time (log n)

Following are the few of excellent books and it is really worth reading them.