Showing posts with label data structure. Show all posts
Showing posts with label data structure. Show all posts

Thursday, January 14, 2016

Binary Tree Level Order Traversal

Pre-order, In-order and Post-order traversal of tree use Depth First Search (DFS). These are called DFS since these techniques visit the tree deeper and deeper until it reaches the leaf node. Level-order traversal (as the name suggest) visits the nodes level by level. Level Order Traversal is Breadth First Search(BFS). Below diagram shows Level Order traversal of a binary tree. 



Let's see implementation techniques:

Without Using External Storage

Applying recursion in BFS traversal is non-trivial. So in this case, the most obvious approach could be to print the nodes at each level iteratively. Above tree has three level, which equals the height of the tree. Now, how do we print all nodes at a given level?

To print nodes at level 2(i.e. 2 and 3), we need to start at the root and keep traversing down the tree recursively. Each time we go one level down the level value needs to be reduced by 1. This way both left and right subtree will keep going down the tree independently until the level becomes 1.

   public void levelOrderIteratively(){
    int height = this.getHeight(root);
    for(int i=1; i<= height; i++){
    printNodesAtGivenLevel(root, i);
    }
    }

//other methods are given in full implementation

The below implementation prints all nodes in the same line. In above method, we are calling a separate method to print for each level so we can format it the way we want (something like below can be easily done by tweaking above for loop. 


Nodes at level 1 = 1 

Nodes at level 2 = 2 3 
Nodes at level 3 = 4 5 6 

Using external storage / Using Queue

One of the de-facto approaches to implementing any breadth-first traversal is using Queue (First in First out data structure). BFS enables to visit nodes of the tree level by level. 

Java Implementation

Below java implementation provides level order traversal using an iterative technique which doesn't require extra storage as well as using Queue. 

package algo;

import java.util.LinkedList;
import java.util.Queue;

/**
 * Generic Binary Tree Implementation
 * @author Siddheshwar
 *
 * @param <E>
 */
public class BinaryTree<E> {
 /**
  * Root node of the tree
  */
 Node<E> root;

 /**
  * Node class to represent each node of the tree
  */
 static class Node<E> {
  E data;
  Node<E> left;
  Node<E> right;

  Node(E d) {
   this.data = d;
   this.left = null;
   this.right = null;
  }
 }

 /**
  * Get height of the tree
  * 
  * @param node
  *            root node
  * @return height of the tree which is max of left subtree height or right
  *         subtree height + 1 (for root)
  */
 public int getHeight(Node<E> node) {
  if (node == null) {
   return 0;
  }

  return Math.max(getHeight(node.left), getHeight(node.right)) + 1;
 }

 /**
  * Print binary tree in level-order
  */
 public void levelOrderIteratively() {
  int height = this.getHeight(root);
  for (int i = 1; i <= height; i++) {
   printNodesAtGivenLevel(root, i);
  }
 }

 /**
  * Print nodes at the given level
  * 
  * @param node
  *            first call with root node
  * @param level
  *            first call with the level for which we need to print
  */
 private void printNodesAtGivenLevel(Node<E> node, int level) {
  if (node == null)
   return;

  if (level == 1) {
   System.out.print(node.data + " ");
   return;
  }

  printNodesAtGivenLevel(node.left, level - 1);
  printNodesAtGivenLevel(node.right, level - 1);

 }

 /**
  * Prints level order traversal using Queue
  */
 public void levelOrderQueue() {
  if (root == null) {
   return;
  }

  Queue<Node<E>> queue = new LinkedList<>();
  queue.add(root);

  while (!queue.isEmpty()) {
   Node<E> node = queue.poll();
   System.out.print(node.data + " ");

   if (node.left != null) {
    queue.add(node.left);
   }
   if (node.right != null) {
    queue.add(node.right);
   }
  }
 }

 /**
  * Method to test the tree construction
  */
 public static void main(String[] args) {
  BinaryTree<Integer> bt = new BinaryTree<Integer>();
  bt.root = new Node<>(1);
  bt.root.left = new Node<>(2);
  bt.root.right = new Node<>(3);
  bt.root.left.left = new Node<>(4);
  bt.root.right.left = new Node<>(5);
  bt.root.right.right = new Node<>(6);

  System.out.println(" height =" + bt.getHeight(bt.root));
  bt.levelOrderIteratively();
  System.out.println();
  bt.levelOrderQueue();
 }
}

--
keep coding !!!

Monday, February 23, 2015

TRIE data structure

TRIE, another Search Tree!

Trie is an infix of the word reTRIEval, and pronounced as TREE but to distinguish it from the tree some people also pronounce it as TRY. As the name suggests it is an efficient retrieval or searching data structure. 

The root node represents an empty string (""), if you go down one level it represents all single character words. Similarly, at level 10, trie represents words or prefixes of 10 letters/characters. It is a search tree where each vertex or node represents a single word or a prefix (but each node actually stores a single letter or character). 


Let's take a sample dictionary of 6 words.
Dictionary, D = {a, an, out, the, them, their}

You can use Hashing or Binary search tree to implement a dictionary,  but here we will use trie to implement it.

Below are trie representations of the above dictionary, D. Both left as well as right trie represents the same dictionary (two different representations). If you want to understand the trie the right one is more suitable and left one represents trie from the implementation point of view. Leaf nodes (colored in blue) represents the last node of a word so 'e' of the word 'the' will be the leaf node. The leaf node signals end of a word, but it can get extended further (like 'a' and 'an'). So every node which is the last character of the word will be the leaf node. The implementation can be achieved by a boolean flag.

Two TRIE representation of the same dictionary

Possible operations:
addWord(String): adds a single word to the trie
search(String): search if the given word or prefix exists in the trie
countWords(String): number of words that match in trie with the given word
countPrefixes(String): number of words that have given the prefix

Implementation

To make it simple, let's consider only English alphabets (a-z); and also make case insensitive. This means each node can have maximum of 26 children. So it's just a generalization of Binary Search Tree.

Java implementation this post, here

Trie implementation provides operations like add a word and find a word. If you understand the fundamentals, implementing other methods is not going to be difficult. 

Analyzing Performance

Cost of looking up a word or prefix in a vocabulary/dictionary implemented using trie depends only on the number of characters in the word/prefix. It doesn't depend on the size of the dictionary. This is an important distinction from the HashMap/HashTable based dictionary implementations.  As shown in the above diagram the cost of finding the word 'the' requires just 3 steps. 

Runtime complexity of lookup = O(length of word)
String comparison is considered as a single operation, so above can be generalized as O(1).

Trie implementation requires more memory than an array or tree. This is because at each level, the node takes space for 26 characters but not all of them will be used. But the space overhead gets compensated with the constant time search performance. 


References:
keep coding !!!

TRIE implementation in Java

I have covered the fundamentals of trie in this post.

/**
 * TRIE implementation in Java 
 * Tries are general search tree where every node
 * can have some pre-defined number of children. Length of children is 26. Max
 * depth of trie is equal to max length of the word in the dictionary
 *
 * Constraints: 1. assume that all words are lowercase (uppercase letters are
 * converted to lowercase) 2. accept only a-z letters—no punctuation, no
 * hyphens, no accents, etc
 * 
 * Main Operations supported: 1. addword(..) : add a word to the dictionary 2.
 * search(..) : check if the given word exists in dictionary
 *
 * @author Siddheshwar
 */
public class Trie {
 private Node root;

 public Trie() {
  root = new Node();
 }

 /**
  * adds a dictionary word to the vocabolary or dictionary
  * 
  * @param word
  */
 public void addWord(String word) {
  if (word == null || !word.matches("[a-zA-Z]+")) { // or matches ("\\w+")
   return;
  }

  Node r = root;
  int index;
  for (char c : word.toLowerCase().toCharArray()) {
   index = this.getIndex(c);

   if (!r.hasChildAt(index)) {
    r = r.addChild(c);
   } else {
    r = r.getChildAt(index);
   }
  }
  r.setAsLeaf();
 }

 /**
  * Get index where the character will get mapped in the array 'a'/'A' will
  * be mapped to 0th, 'z'/'Z' will get mapped to 25th index
  * 
  * @param ch
  * @return index
  */
 private int getIndex(char ch) {
  // return (int)(ch - 'a');
  return String.valueOf(ch).codePointAt(0)
    - String.valueOf('a').codePointAt(0);
 }

 /**
  * Is word present in the trie
  * 
  * @param word
  * @return
  */
 public boolean contains(String word) {
  if (word == null)
   return true;
  Node r = root;
  for (char c : word.toCharArray()) {
   r = r.getChildAt(this.getIndex(c));
   if (r == null) {
    return false;
   }
  }
  return true;
 }

 /**
  * Returns the node (or null if word is not found)
  * 
  * @param word
  * @return Node
  */
 public Node getNodeEndingWithWord(String word) {
  if (word == null)
   return null;
  Node r = root;
  for (char c : word.toCharArray()) {
   r = r.getChildAt(this.getIndex(c));
   if (r == null) {
    return null;
   }
  }
  return r;
 }
}

import java.util.Arrays;
import java.util.List;

/**
 * Vertex or Node of a TRIE tree. Supports only alphabets a-z (ignores case) 'a'
 * occupies 0th position and 'z', 25th in Node array(i.e. children)
 */
public class Node {
 private Node[] childen;
 private boolean leaf;
 private char value;
 static final char NULL_CHAR = '\0';

 public Node() {
  childen = new Node[26];
  leaf = false;
  value = NULL_CHAR;
 }

 public Node(char c) {
  childen = new Node[26];
  leaf = false;
  value = c;
 }

 public List<Node> getChilden() {
  return Arrays.asList(childen);
 }

 public boolean hasChildAt(int index) {
  return this.childen[index] == null ? false : true;
 }

 public boolean isLeaf() {
  return leaf;
 }

 public char getValue() {
  return value;
 }

 public Node getChildAt(int index) {
  return this.childen[index];
 }

 public void setAsLeaf() {
  this.leaf = true;
 }

 public boolean isCharValid() {
  return this.value != NULL_CHAR ? true : false;
 }

 /**
  * add a child node to the current node
  * 
  * @param ch
  * @return Node
  */
 public Node addChild(char ch) {
  Node childNode = new Node(ch);
  // int index = (int)(ch - 'a'); prefer below approach
  int index = String.valueOf(ch).codePointAt(0)
    - String.valueOf('a').codePointAt(0);
  this.childen[index] = childNode;
  return childNode;
 }

 /**
  * Get count of valid children; upper limit is 26.
  * 
  * @return count
  */
 public int getChildrenCount() {
  int count = 0;
  for (int i = 0; i < 26; i++) {
   if (this.hasChildAt(i)) {
    count++;
   }
  }
  return count;
 }

 /**
  * Returns comma separated list of children for the node
  * 
  * @return
  */
 public String getChildren() {
  StringBuilder output = new StringBuilder();
  for (int i = 0; i < 26; i++) {
   if (this.hasChildAt(i)) {
    output.append((char) (i + 65)).append(",");
   }
  }
  return output.substring(0, output.length() - 1);
 }
}

Monday, September 8, 2014

Bit Vector

The bit vector is also referred to as bit-array or bit-set. As the name suggests, it's array of bits. Every modern programming language supports primitive or basic data types like byte, integer, short etc which are inherently array of bits only. So, is bit-array different? 

A byte is composed of 8-bits. Literally, byte data type is also a bit-array of 8 bits or 1 byte.  Integer data type of 4 bytes is a bit-array of 32 bits and so on. This is where the similarity stops and life of bit-array begins. 

To understand it further let's take an example. Assume that you want to represent a set of integers in the range of 0 and 999 (both are inclusive). An option which you have as a programmer is; store these integers in an array of length 1000 or a list which can grow up to 1000. Remember, it's a set, so you are going to store only unique numbers in the range. 

//Java snippet
int[] set = new int[1000];
set[i] = num

What is the memory requirement of above?
Each integer takes 4 bytes. So total memory requirement for 1000 integers is 4KB (=4*1000 byte).  

can we improve? 
If the requirement says that not all numbers in the range might be present in the set, we can use ArrayList instead of an array. So this will improve a bit, as the numbers which are not in the set will not occupy any memory. This helps to an extent but worst-case memory requirement is still 4KB. 

can we improve?
As long as you going to store each element of the set in an integer, space overhead is going to remain the same.

Bit-Array

A bit can only take two values i.e. 0 or 1.  It can be used to denote the presence or absence of an item. So as discussed above, a byte is an array of 8 bits. This means these 8 bits can be used to represent presence/absence of an item in the set. Recall that in an integer array, the index is used to get element stored at that location. Similarly, in bit-set, bit at a position can be used to represent the presence of a number.

So as shown in the above figure, we can represent a set of integers in the range of 0 and 7, can be represented by just 1 byte ( or 8 bits). If the number is present make bit at the corresponding bit as 1. 

So below two operations can be performed on bit-set.
  1. Setting an integer in the array
  2. Testing if an integer exists in the array/set

Implementing Bit-vector

Now, let's return to our initial problem of storing integers in the range of 0 and 999. 

Number of bits required = 1000
Number of bytes required = 1000/8 = 125
So 1000 integers can be represented in 125 bytes.

Below is the Java implementation:

/**
 * Bit-vector to store integers
 * 
 * @author Siddheshwar
 * 
 */
public class BitArray {
 private byte[] set;

 public BitArray(int length) {
  int arraysize = length >> 3;

  // if length is not multiple of 8
  if (length % 8 != 0) {
   arraysize++;
  }
  this.set = new byte[arraysize];
 }

 /**
  * Set given number in the bit-array i.e. make corresponding bit as 1
  * 
  * @param number
  */
 public void storeNumber(int number) {
  if (number > (set.length << 3) || number < 0) {
   throw new IllegalArgumentException("number out of range");
  }

  int arrayIndex = this.getArrayIndex(number);
  int bitIndex = this.getBitIndex(number);

  // shift 1 to the bit index
  int pos = 1 << bitIndex;

  // so bit at position needs to be made 1
  this.set[arrayIndex] = (byte) (this.set[arrayIndex] | pos);
 }

 /**
  * Check if the number exists in the array if the corresponding bit is 1
  * then number exists
  * 
  * @param number
  */
 public boolean numberExists(int number) {
  if (number > (set.length << 3) || number < 0) {
   throw new IllegalArgumentException("number out of range");
  }

  int arrayIndex = this.getArrayIndex(number);
  int bitIndex = this.getBitIndex(number);

  // shift 1 to the bit index
  int pos = 1 << bitIndex;

  // check bit at position is 0 or 1
  return (this.set[arrayIndex] & pos) > 0 ? true : false;
 }

 private int getArrayIndex(int number) {
  return number >> 3; // divide by 8
 }

 private int getBitIndex(int number) {
  return number % (1 << 3); // % 8
 }

 private void printSet() {
  System.out.println("\n array content :");
  for (int i = 0; i < set.length; i++)
   System.out.print(Integer.toBinaryString(this.set[i]));
 }

 //test method
 public static void main(String[] args) {
  BitArray array = new BitArray(1000);
  System.out.println("length of array :" + array.set.length);

  int num1 = 17;
  array.storeNumber(num1);

  int num2 = 171;
  array.storeNumber(num2);

  int num3 = 400;
  array.storeNumber(num3);

  System.out.println("val " + num1 + " exists ? "
    + array.numberExists(num1));
  System.out.println("val " + num2 + " exists ? "
    + array.numberExists(num2));
  System.out.println("val " + num3 + " exists ? "
    + array.numberExists(num3));
  System.out.println("val " + 500 + " exists ? "
    + array.numberExists(500));

  for (int i = 0; i < 1000; i = i + 2)
   array.storeNumber(i);

  array.printSet();

  /*
   * for(int i = 0; i< 1000; i++) System.out.println(array.get(i));
   */
 }
}

Output:
length of array :125
val 17 exists ? true
val 171 exists ? true
val 400 exists ? true
val 500 exists ? false

 array content :
10101011010101101011110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110111011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101101010110101011010101


---
keep coding !!!

Thursday, July 31, 2014

Graph Implementation in Java

  1. In mathematics, and more specifically in graph threoy, a graph is a representation of set of objects where some pair of objects are connected by links - Wikipedia.
The graph is important for modeling any kind of relationship. So you can model road network between cities, the relationship among people (known as friendship graph) etc.

G = (V,E) 
A graph is defined as set of vertices V and set of edges (or vertex pair) E.


Let's take an example of flights between different cities, vertices represent cities and edges represent weight/cost of travel between a pair of cities. The graph can be directed if the edge(x,y) is not equivalent to the edge(y,x) otherwise it will undirected. Edges can also have the cost or weight associated with it.

Modelling the graph appropriately is quite important. Two basic data structures are - adjacency matrices and adjacency lists. Below is a weighted directed graph which represents the weighted cost of air travel between cities.


BL: Bangalore, HK: Hong Kong etc.



Adjacency Matrix represents G as an N x N matrix and matrix[i][j] = 1 if (i,j) is an edge of G, and 0 if it's not. If the graph is weighted then weight will be the value of the cell (assume that 0 means no connection). Adjacency List uses linked data structure to stores neighbors adjacent to each vertex.  If you are using adjacency list implementation to store undirected graph, the same edge (u,v) appears twice so that's extra space overhead. Each has its own pluses and minuses so which data structure you chose depends on what problem you going to solve. So think closely about the problem first before deciding the data structure. Practically adjacency list is right data structure for most of the applications.
But, the question like is (i,j) in graph? can be answered more easily if implementation is through Adjacency Matrix. 

Graph Implementation

Implementing the Adjacency Matrix is trivial, we just need a 2-dimensional array. This post, I will be implementing graph using adjacency list. Also, will be using two different approaches. 

Approach 1: The simplest possible adjacency list implementation could be a Graph class which has a collection of vertices and edges.


package graph.algo;

/**
 * Vertex represents points in a graph
 * 
 * @author Siddheshwar
 * @param <V>
 */
public class Vertex<V> {
 private V name;

 public Vertex(V name) {
  super();
  this.name = name;
 }

 public V getName() {
  return name;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((name == null) ? 0 : 
                         name.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Vertex other = (Vertex) obj;
  if (name == null) {
   if (other.name != null)
    return false;
  } else if (!name.equals(other.name))
   return false;
  return true;
 }
}


package graph.algo;

/**
 * Represents connection between two vertices.
 * 
 * @author Siddheshwar
 * @param <V>
 */
public class Edge<V> {
 private Vertex<V> source;
 private Vertex<V> destination;
 int weight;

 public Edge(Vertex<V> source, Vertex<V> destination, int weight) {
  super();
  this.source = source;
  this.destination = destination;
  this.weight = weight;
 }

 public Vertex<V> getSource() {
  return source;
 }

 public Vertex<V> getDestination() {
  return destination;
 }

 public int getWeight() {
  return weight;
 }

 @Override
 public String toString() {
      return "Edge [source=" + source + ", destination=" + destination
    + ", weight=" + weight + "]";
 }
}


package graph.algo;

import java.util.ArrayList;
import java.util.List;

/**
 * Graph object as collection of vertices and edges
 * 
 * @author Siddheshwar
 * @param <V>
 */
public class GraphSimple<V> {
 private List<Vertex<V>> vertices;
 private List<Edge<V>> edges;

 public GraphSimple() {
  super();
  this.vertices = new ArrayList<>();
  this.edges = new ArrayList<>();
 }

 // other methods...
}


Approach 2: This approach uses the Map API of Java to create the relationship. The mapping between a vertex and node forms Edge. There is no explicit class to represent edges. So, source vertex is represented by vertex and the edge from source to target is represented by Node (by storing vertex as key and Node as value in map). The relationship or edge, in this case, gets represented by key-value in the map.

                     
package graph.algo;

/**
 * Node class to represent nodes in the graph. It will help in forming edges
 * 
 * @author Siddheshwar
 * 
 * @param <V>
 */
public class Node<V> {
 V name; // Vertex name
 int weight;

 public Node(V name, int weight) {
  super();
  this.name = name;
  this.weight = weight;
 }
 
 public V getName() {
  return name;
 }

 public int getWeight() {
  return weight;
 }

 @Override
 public String toString() {
  return "(" + this.weight + ")" + this.name;
 }
}


package graph.algo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Graph object which maps vertex with nodes
 * 
 * @author Siddheshwar
 * 
 * @param <V>
 */
public class Graph<V> {
 // Vertex i.e. V gets mapped to list of all connecting Nodes. 
 Map<V, List<Node<V>>> adjacencyList;
 int verticesCount;
 int edgeCount;

 public Graph() {
  super();
  this.adjacencyList = new HashMap<>();
 }

 /**
  * Add a new node for the given vertex. 
         *  Vertex to node connection forms Edge.
  * 
  * @param vertex
  * @param node
  */
 public void addNewNode(V vertex, Node<V> node) {
  List<Node<V>> nodes = adjacencyList.get(vertex);
  if (nodes == null || nodes.isEmpty()) { 
   nodes = new ArrayList<Node<V>>();
   nodes.add(node);
  } else {
   nodes.add(node);
  }
  adjacencyList.put(vertex, nodes);
 }

 /**
  * Takes two vertices and checks if there is a path between v1 and v2.
  * Doesn't take vice-versa.
  * 
  * @param v1
  *            first vertex
  * @param v2
  *            second vertex
  * @return
  */
 public boolean hasRelationship(V v1, V v2) {
  if (v1 == null && v2 == null)
   return true;
  if (v1 != null && v2 == null)
   return true;
  if (v1 == null && v2 != null)
   return false;

  List<Node<V>> nodes = null;

  if (adjacencyList.containsKey(v1)) {
   nodes = adjacencyList.get(v1);
   if (nodes != null || !nodes.isEmpty()) {
    for (Node<V> v : nodes) {
     if (v.getName().equals(v2))
      return true;
    }
   }
  }
  return false;
 }

 public void print() {
  System.out.println("Graph is --->");
  for (V v : adjacencyList.keySet()){
      System.out.println(v + " --- " + adjacencyList.get(v));
                }  
 }

 //Test method
 public static void main(String[] args) {
       Graph<String> graph = new Graph<String>();
       graph.addNewNode("Bangalore", new Node<String>("SFO", 100));
       graph.addNewNode("Bangalore", new Node<String>("HongKong", 50));
       graph.addNewNode("Bangalore", new Node<String>("LA", 70));
       graph.addNewNode("LA", new Node<String>("SFO", 20));
       graph.addNewNode("HongKong", new Node<String>("LA", 60));

       graph.print();

      System.out.println(" Path between Bangalore and LA exists ? :"
    + graph.hasRelationship("Bangalore", "LA"));
 }

}

Output:
Graph is --->
HongKong --- [(60)LA]
LA --- [(20)SFO]
Bangalore --- [(100)SFO, (50)HongKong, (70)LA]

 The path between Bangalore and LA exists? : true


This was a simplistic implementation of Graph, as I wanted to stress on fundamentals. This post has more refined Adjacency List graph implementation in Java. 


---
do post your feedback!!!