Thursday, August 20, 2015

Leetcode: Graph Valid Tree

Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree.
For example:
Given n = 5 and edges = [[0, 1], [0, 2], [0, 3], [1, 4]], return true.
Given n = 5 and edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]], return false.
Hint:
  1. Given n = 5 and edges = [[0, 1], [1, 2], [3, 4]], what should your return? Is this case a valid tree?
  2. According to the definition of tree on Wikipedia: “a tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without simple cycles is a tree.”
Note: you can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together inedges.
Understand the problem:
A classic graph search problem. The key is first to transform from the edge list to the adjecent list. The problem is equivalent to whether the graph exists a circle. We could solve the problem by using either DFS or BFS. 

A DFS solution:
public class Solution {
    public boolean validTree(int n, int[][] edges) {
        
        // Create an adj list 
        List<List<Integer>> adjList = new ArrayList<List<Integer>>();
        for (int i = 0; i < n; i++) {
            adjList.add(new ArrayList<Integer>());
        }
        
        for (int[] edge : edges) {
            adjList.get(edge[1]).add(edge[0]);
            adjList.get(edge[0]).add(edge[1]);
        }
        
        boolean[] visited = new boolean[n];
        
        if (!validTreeHelper(n, edges, 0, -1, visited, adjList)) {
            return false;
        }
        
        // Check the islands
        for (boolean v : visited) {
            if (!v) {
                return false;
            }
        }
        
        return true;
    }
    
    private boolean validTreeHelper(int n, int[][] edges, int vertexId, int parentId, 
                                    boolean[] visited, List<List<Integer>> adjList) {
        if (visited[vertexId]) {
            return false;
        }
        
        visited[vertexId] = true;
        
        List<Integer> neighbors = adjList.get(vertexId);
        for (Integer neighbor : neighbors) {
            if (neighbor != parentId && !validTreeHelper(n, edges, neighbor, vertexId, visited, adjList)) {
                return false;
            }
        }
        
        return true;
    }
}

A BFS solution:
public class Solution {
    public boolean validTree(int n, int[][] edges) {
        
        // Create an adj list 
        List<List<Integer>> adjList = new ArrayList<List<Integer>>();
        for (int i = 0; i < n; i++) {
            adjList.add(new ArrayList<Integer>());
        }
        
        for (int[] edge : edges) {
            adjList.get(edge[1]).add(edge[0]);
            adjList.get(edge[0]).add(edge[1]);
        }
        
        boolean[] visited = new boolean[n];
        
        Queue<Integer> queue = new LinkedList<Integer>();
        queue.offer(0);
        
        while (!queue.isEmpty()) {
            int vertexId = queue.poll();
            
            if (visited[vertexId]) {
                return false;
            }
            
            visited[vertexId] = true;
            
            for (int neighbor : adjList.get(vertexId)) {
                if (!visited[neighbor]) {
                    queue.offer(neighbor);
                }
            }
        }
        
        // Check the islands
        for (boolean v : visited) {
            if (!v) {
                return false;
            }
        }
        
        return true;
    }
}

If a graph is valid binary tree, it must follow the two conditions:
1. num of edges = num of nodes - 1
2. There is only 1 CC

BFS Solution:
public class Solution {
    /**
     * @param n: An integer
     * @param edges: a list of undirected edges
     * @return: true if it's a valid tree, or false
     */
    public boolean validTree(int n, int[][] edges) {
        // write your code here
        if (n == 0) {
            return edges == null || edges.length == 0;
        }
        
        if (edges.length != n - 1) {
            return false;
        }
        
        Set<Integer> visited = new HashSet<>();
        
        List<List<Integer>> adjList = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            adjList.add(new ArrayList<>());
        }
        
        // add nodes to the adjList
        for (int[] edge : edges) {
            int from = edge[0];
            int to = edge[1];
            
            adjList.get(from).add(to);
            adjList.get(to).add(from);
        }
        
        // start bfs from each node, if it's not visited
        bfs(adjList, 0, visited);
        
        return visited.size() == n;
    }
    
    private void bfs(List<List<Integer>> adjList, int root, Set<Integer> visited) {
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(root);
        visited.add(root);
        
        while (!queue.isEmpty()) {
            int node = queue.poll();
            for (int neighbor : adjList.get(node)) {
                if (visited.contains(neighbor)) {
                    continue;
                }
                
                queue.offer(neighbor);
                visited.add(neighbor);
            }
        }
    }
}
Update on 1/11/2021: Union-Find

public class Solution {
    /**
     * @param n: An integer
     * @param edges: a list of undirected edges
     * @return: true if it's a valid tree, or false
     */
    public boolean validTree(int n, int[][] edges) {
        // write your code here
        if (n == 0) {
            return edges == null || edges.length == 0;
        }
        
        if (edges.length != n -1) {
            return false;
        }
        
        // get number of cc 
        UF uf = new UF(n);
        
        for (int[] edge : edges) {
            uf.union(edge[0], edge[1]);
        }
        
        return uf.getNumCC() == 1;
    }
}

class UF {
    private int n;
    private int[] parents;
    private int numCC;
    
    public UF(int n) {
        this.n = n;
        parents = new int[n];
        numCC = n;
        
        for (int i = 0; i < n; i++) {
            parents[i] = i;
        }
    }
    
    public int find(int x) {
        int root = x;
        while (parents[root] != root) {
            root = parents[root];
        }
        
        // path compression
        while (x != root) {
            int temp = parents[x];
            parents[x] = root;
            x = temp;
        }
        
        return root;
    }
    
    public void union(int x, int y) {
        int px = find(x);
        int py = find(y);
        
        if (px != py) {
            parents[px] = py;
            numCC--;
        }
    }
    
    public int getNumCC() {
        return numCC;
    }
}

Leetcode: Kth Smallest Element in a BST

Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.
Note: 
You may assume k is always valid, 1 ≤ k ≤ BST's total elements.
Follow up:
What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine?
Hint:
  1. Try to utilize the property of a BST.
  2. What if you could modify the BST node's structure?
  3. The optimal runtime complexity is O(height of BST).
A similar post can be found at : http://buttercola.blogspot.com/2014/10/facebook-print-n-th-node-in-binary-tree.html

Code (Java):
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    private int counter = 0;
    private boolean found = false;
    private int val = Integer.MIN_VALUE;
    public int kthSmallest(TreeNode root, int k) {
        if (root == null) {
            return 0;
        }
        
        kthSmallestHelper(root, k);
        
        return val;
    }
    
    private void kthSmallestHelper(TreeNode root, int k) {
        if (root == null) {
            return;
        }
        
        if (!found) {
            kthSmallestHelper(root.left, k);
        }
        
        counter++;
        if (counter == k) {
            found = true;
            val = root.val;
        }
        
        if (!found) {
            kthSmallestHelper(root.right, k);
        }
    }
}

An O(h) solution:
If the BST node's structure can be modified. We let each node maintain the number of nodes of its left subtree. Therefore, for each node, we can compare k with the number of its subtree. 

Code (Java):
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public int kthSmallest(TreeNode root, int k) {
        if (root == null) {
            return 0;
        }
        
        int leftNodes = getNumberNodes(root.left);
        if(k == leftNodes + 1) {
            return root.val;
        } else if (k > leftNodes + 1) {
            return kthSmallest(root.right, k - leftNodes - 1);
        } else {
            return kthSmallest(root.left, k);
        }
    }
    
    private int getNumberNodes(TreeNode root) {
        if (root == null) {
            return 0;
        }
        
        return getNumberNodes(root.left) + getNumberNodes(root.right) + 1;
    }
}
Update on 4/29/19:
/**
 * Definition of TreeNode:
 * public class TreeNode {
 *     public int val;
 *     public TreeNode left, right;
 *     public TreeNode(int val) {
 *         this.val = val;
 *         this.left = this.right = null;
 *     }
 * }
 */

public class Solution {
    /**
     * @param root: the given BST
     * @param k: the given k
     * @return: the kth smallest element in BST
     */
    public int kthSmallest(TreeNode root, int k) {
        // write your code here
        BSTNode bstRoot = buildBST(root);

        BSTNode p = bstRoot;

        while (p != null) {
            int numLeft = p.left == null ? 0 : p.left.numNodes;
            int numRight = p.right == null ? 0 : p.right.numNodes;

            if (k == numLeft + 1) {
                return p.val;
            }

            if (k <= numLeft) {
                p = p.left;
            } else {
                p = p.right;
                k = k- numLeft - 1;
            }
        }
        
        return -1;
    }

    private BSTNode buildBST(TreeNode root) {
        if (root == null) {
            return null;
        }

        BSTNode left = buildBST(root.left);
        BSTNode right = buildBST(root.right);

        int numNodes = (left == null ? 0 : left.numNodes) + 
                       (right == null ? 0 : right.numNodes) + 
                       1;

        BSTNode bstRoot = new BSTNode(root.val, numNodes);

        bstRoot.left = left;
        bstRoot.right = right;

        return bstRoot;
    }
}

class BSTNode {
    int val;
    int numNodes;
    BSTNode left, right;

    public BSTNode(int val, int numNodes) {
        this.val = val;
        this.numNodes = numNodes;
        left = right = null;
    }
}

Wednesday, August 19, 2015

Leetcode: Meeting Rooms

Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), determine if a person could attend all meetings.
For example,
Given [[0, 30],[5, 10],[15, 20]],
return false.
Understand the problem:
The problem looks very similar to the merge interval and insert intervals. So the idea is still the same: first sort the intervals according to the start times, then check if there is any overlap. 

Code (Java):
/**
 * Definition for an interval.
 * public class Interval {
 *     int start;
 *     int end;
 *     Interval() { start = 0; end = 0; }
 *     Interval(int s, int e) { start = s; end = e; }
 * }
 */
public class Solution {
    public boolean canAttendMeetings(Interval[] intervals) {
        if (intervals == null || intervals.length ==0) {
            return true;
        }
        
        // Sort according to the start time
        Arrays.sort(intervals, new IntervalComparator());
        
        Interval prev = intervals[0];
        for (int i = 1; i < intervals.length; i++) {
            Interval curr = intervals[i];
            if (isOverlapped(prev, curr)) {
                return false;
            }
            prev = curr;
        }
        
        return true;
    }
    
    public class IntervalComparator implements Comparator<Interval> {
        @Override
        public int compare(Interval a, Interval b) {
            return a.start - b.start;
        }
    }
    
    private boolean isOverlapped(Interval a, Interval b) {
        return a.end > b.start;
    }
}

Leetcode: Missing Ranges

Given a sorted integer array where the range of elements are [lowerupper] inclusive, return its missing ranges.
For example, given [0, 1, 3, 50, 75]lower = 0 and upper = 99, return ["2", "4->49", "51->74", "76->99"].
Understand the problem:
The problem itself is not hard at all. The key is to handle several corner cases. e.g. 
 -- If the array is empty, the missing ranges should be from lower to upper, inclusive. 
 -- For the leading missing range, e.g. -2 , [-1], -1. The output should be "-2". Note that the lower bound is inclusive. 
 -- For the trailing missing range, e.g. -2, [-2], 1, the output should be "-1->1". The upper bound is inclusive as well. 

Code (Java):
public class Solution {
    public List<String> findMissingRanges(int[] nums, int lower, int upper) {
        List<String> result = new ArrayList<String>();
        if (nums == null || nums.length == 0) {
            outputToResult(lower, upper, result);
            return result;
        }
        
        // leading missing range
        if (nums[0] - lower > 0) {
            outputToResult(lower, nums[0] - 1, result);
        }
        
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] - nums[i - 1] > 1) {
                outputToResult(nums[i - 1] + 1, nums[i] - 1, result);
            }
        }
        
        // trailing missage ranges
        if (upper - nums[nums.length - 1] > 0) {
            outputToResult(nums[nums.length - 1] + 1, upper, result);
        }
        
        return result;
    }
    
    private void outputToResult(int start, int end, List<String> result) {
        StringBuffer sb = new StringBuffer();
        if (start == end) {
            sb.append(start);
        } else {
            sb.append(start + "->" + end);
        }
        
        result.add(sb.toString());
    }
}

Leetcode: The Skyline Problem

A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. Now suppose you are given the locations and height of all the buildings as shown on a cityscape photo (Figure A), write a program to output the skyline formed by these buildings collectively (Figure B).
Buildings Skyline Contour
The geometric information of each building is represented by a triplet of integers [Li, Ri, Hi], where Li and Ri are the x coordinates of the left and right edge of the ith building, respectively, and Hi is its height. It is guaranteed that 0 ≤ Li, Ri ≤ INT_MAX0 < Hi ≤ INT_MAX, and Ri - Li > 0. You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0.
For instance, the dimensions of all buildings in Figure A are recorded as: [ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] .
The output is a list of "key points" (red dots in Figure B) in the format of [ [x1,y1], [x2, y2], [x3, y3], ... ] that uniquely defines a skyline. A key point is the left endpoint of a horizontal line segment. Note that the last key point, where the rightmost building ends, is merely used to mark the termination of the skyline, and always has zero height. Also, the ground in between any two adjacent buildings should be considered part of the skyline contour.
For instance, the skyline in Figure B should be represented as:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ].
Notes:


  • The number of buildings in any input list is guaranteed to be in the range [0, 10000].
  • The input list is already sorted in ascending order by the left x position Li.
  • The output list must be sorted by the x position.
  • There must be no consecutive horizontal lines of equal height in the output skyline. For instance, [...[2 3], [4 5], [7 5], [11 5], [12 7]...] is not acceptable; the three lines of height 5 should be merged into one in the final output as such: [...[2 3], [4 5], [12 7], ...]
Understand the problem:
The problem looks quite tricky. It looks very like the segment intervals problem. For this kind of problems, one general solution is :
  -- First, split the left key point and the right key point into two parts and store into another data structure. For this problem, [2 9 10] can be split into [2, 10, L] and [9, 10, R]
  -- Then, sort the split list according to the x coordinate, if equal, sort by height. Note that for this problem, if two x coordinates are Left end, the greater height should be before the lower, because the lower height is hidden. If both are Right end, put the lower first. 
  -- Thirdly, iterate the sorted list. If the end point is left. Put into the priority queue. Note that if the priority queue is empty or the height of the end point is greater than the peek of the pq, put the end point into result list. If the end point is right. Remove the point from pq. If pq is empty then, put a <x, 0> into result list. Else, if the height of the end point is greater than the peek of the pq, put <x, peek()> into the result list. 

Code (Java):
public class Solution {
    private class Edge {
        private int x;
        private int height;
        private boolean isLeft;
        
        // Constructor
        public Edge(int x, int height, boolean isLeft) {
            this.x = x;
            this.height = height;
            this.isLeft = isLeft;
        }
    }
    
    public List<int[]> getSkyline(int[][] buildings) {
        List<int[]> result = new ArrayList<int[]>();
        
        if (buildings == null || buildings.length == 0 || buildings[0].length == 0) {
            return result;
        }
        
        PriorityQueue<Integer> pq = new PriorityQueue<>(1000, Collections.reverseOrder());
        
        // Parse buildings and fill the edges
        List<Edge> edges = new ArrayList<Edge>();
        for (int[] building : buildings) {
            Edge leftEdge = new Edge(building[0], building[2], true);
            edges.add(leftEdge);
            
            Edge rightEdge = new Edge(building[1], building[2], false);
            edges.add(rightEdge);
        }
        
        // Sort the edges according to the left keypoint
        Collections.sort(edges, new EdgeComparator());
        
        // Iterate all sorted edges
        for (Edge edge : edges) {
            if (edge.isLeft) {
                if (pq.isEmpty() || edge.height > pq.peek()) {
                    result.add(new int[]{edge.x, edge.height});
                }
                pq.offer(edge.height);
            } else {
                pq.remove(edge.height);
                if (pq.isEmpty()) {
                    result.add(new int[]{edge.x, 0});
                } else if (edge.height > pq.peek()) {
                    result.add(new int[]{edge.x, pq.peek()});
                }
            }
        }
        
        return result;
    }
    
    public class EdgeComparator implements Comparator<Edge> {
        @Override
        public int compare(Edge a, Edge b) {
            if (a.x != b.x) {
                return a.x - b.x;
            }
            
            if (a.isLeft && b.isLeft) {
                return b.height - a.height;
            }
            
            if (!a.isLeft && !b.isLeft) {
                return a.height - b.height;
            }
            
            return a.isLeft ? -1 : 1;
        }
    }
}

Analysis:
Now let's analyze the time and the space complexity of the solution. Sorting the 2 * N list takes O(nlogn) time. For each of the 2 * n edges, peek() the max takes O(1) time but add and remove takes O(log n) time. So the overall time complexity is O(n * logn). 

An alternative solution:
Notice that "key points" are either the left or right edges of the buildings. Therefore, we first obtain both the edges of all the N buildings, and store the 2N edges in a sorted array. Maintain a max-heap of building heights while scanning through the edge array: If the current edge is a left edge, then add the height of its associated building to the max-heap; if the edge is a right one, remove the associated height from the heap. Then we take the top value of the heap (yi) as the maximum height at the current edge position (xi). Now (xi, yi) is a potential key point. If yi is the same as the height of the last key point in the result list, it means that this key point is not a REAL key point, but rather a horizontal continuation of the last point, so it should be discarded; otherwise, we add (xi,yi) to the result list because it is a real key point. Repeat this process until all the edges are checked.
It takes O(NlogN) time to sort the edge array. For each of the 2N edges, it takes O(1) time to query the maximum height but O(logN) time to add or remove elements. Overall, this solution takes O(NlogN) time.
Code(Java):
public class Solution {
    private class Edge {
        private int x;
        private int height;
        private boolean isLeft;
        
        // Constructor
        public Edge(int x, int height, boolean isLeft) {
            this.x = x;
            this.height = height;
            this.isLeft = isLeft;
        }
    }
    
    public List<int[]> getSkyline(int[][] buildings) {
        List<int[]> result = new ArrayList<int[]>();
        
        if (buildings == null || buildings.length == 0 || buildings[0].length == 0) {
            return result;
        }
        
        PriorityQueue<Integer> pq = new PriorityQueue<>(1000, Collections.reverseOrder());
        
        // Parse buildings and fill the edges
        List<Edge> edges = new ArrayList<Edge>();
        for (int[] building : buildings) {
            Edge leftEdge = new Edge(building[0], building[2], true);
            edges.add(leftEdge);
            
            Edge rightEdge = new Edge(building[1], building[2], false);
            edges.add(rightEdge);
        }
        
        // Sort the edges according to the left keypoint
        Collections.sort(edges, new EdgeComparator());
        
        pq.offer(0);
        int prev = 0;
        
        // Iterate all sorted edges
        for (Edge edge : edges) {
            if (edge.isLeft) {
                pq.offer(edge.height);
            } else {
                pq.remove(edge.height);
            }
            
            int curr = pq.peek();
            
            if (curr != prev) {
                result.add(new int[]{edge.x, curr});
                prev = curr;
            }
        }
        
        return result;
    }
    
    public class EdgeComparator implements Comparator<Edge> {
        @Override
        public int compare(Edge a, Edge b) {
            if (a.x != b.x) {
                return a.x - b.x;
            }
            
            if (a.isLeft && b.isLeft) {
                return b.height - a.height;
            }
            
            if (!a.isLeft && !b.isLeft) {
                return a.height - b.height;
            }
            
            return a.isLeft ? -1 : 1;
        }
    }
}

Update on 3/27:
public class Solution {
    /**
     * @param buildings: A list of lists of integers
     * @return: Find the outline of those buildings
     */
    public List<List<Integer>> buildingOutline(int[][] buildings) {
        if (buildings == null || buildings.length == 0) {
            return new ArrayList<>();
        }

        List<Event> events = new ArrayList<>();
        for (int[] building : buildings) {
            events.add(new Event(building[0], building[2], 0));
            events.add(new Event(building[1], building[2], 1));
        }

        Collections.sort(events, new MyEventComparator());

        PriorityQueue<Integer> pq = new PriorityQueue<>(Collections.reverseOrder()); // pq store the heights
        Map<Integer, Integer> map = new HashMap<>(); // <height, refCount>
        List<List<Integer>> ans = new ArrayList<>();

        for (Event event : events) {
            if (event.flag == 0) {
                if (pq.isEmpty() || event.y > pq.peek()) {
                    List<Integer> list = new ArrayList<>();
                    list.add(event.x);
                    list.add(event.y);
                    ans.add(list);
                }

                if (map.containsKey(event.y)) {
                    int count = map.get(event.y);
                    count++;
                    map.put(event.y, count);
                } else {
                    map.put(event.y, 1);
                    pq.offer(event.y);
                }
            } else {
                int count = map.get(event.y);
                if (count == 1) {
                    pq.remove(event.y);
                    map.remove(event.y);
                } else {
                    map.put(event.y, count - 1);
                }
                if (pq.isEmpty()) {
                    List<Integer> list = new ArrayList<>();
                    list.add(event.x);
                    list.add(0);
                    ans.add(list);
                } else if (event.y > pq.peek()) {
                    List<Integer> list = new ArrayList<>();
                    list.add(event.x);
                    list.add(pq.peek());
                    ans.add(list);
                }
            }
        }
        
        List<List<Integer>> ans2 = new ArrayList<>();
        for (int i = 0; i < ans.size() - 1; i++) {
            if (ans.get(i).get(1) > 0) {
                List<Integer> list = new ArrayList<>();
                list.add(ans.get(i).get(0));
                list.add(ans.get(i + 1).get(0));
                list.add(ans.get(i).get(1));
                
                ans2.add(list);
            }
        }
        
        return ans2;
    }
}

class Event {
    int x;
    int y;
    int flag; // 0 start 1 end

    public Event(int x, int y, int flag) {
        this.x = x;
        this.y = y;
        this.flag = flag;
    }
}

class MyEventComparator implements Comparator<Event> {
    @Override
    public int compare(Event a, Event b) {
        if (a.x != b.x) {
            return a.x - b.x;
        }
        

        if (a.flag == 0 && b.flag == 0) {
            return b.y - a.y;
        }

        if (a.flag == 1 && b.flag == 1) {
            return a.y - b.y;
        }

        return a.flag == 0 ? -1 : 1;
    }
}

Some corner cases to consider:
1. In the compare function, what if x is the same? Then we need to determine which one first.
2. What if we insert cities with the same heights? We need a ref count to maintain the current max height

Tuesday, August 18, 2015

Leetcode: Number of Islands

Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Example 1:
11110
11010
11000
00000
Answer: 1
Example 2:
11000
11000
00100
00011
Answer: 3
Understand the problem:
This problem is an alternative view of connected components in a undirected graph. It could be easily solved by using either DFS or BFS. 

DFS Solution (Java):
public class Solution {
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return 0;
        }
        int rows = grid.length;
        int cols = grid[0].length;
        
        boolean[][] visited = new boolean[rows][cols];
        int result = 0;
        
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (grid[i][j] == '1' && !visited[i][j]) {
                    result++;
                    numIslandsHelper(grid, visited, i, j, rows, cols);
                }
            }
        }
        
        return result;
    }
    
    private void numIslandsHelper(char[][] grid, boolean[][] visited, int i, int j, int numRows, int numCols) {
        if (i < 0 || i >= numRows) {
            return;
        }
        
        if (j < 0 || j >= numCols) {
            return;
        }
        
        if (visited[i][j]) {
            return;
        }
        
        // If water
        if (grid[i][j] == '0') {
            return;
        }
        
        // Mark the visted[i][j] = true
        visited[i][j] = true;
        
        // Go up, down, left and right
        numIslandsHelper(grid, visited, i - 1, j, numRows, numCols);
        numIslandsHelper(grid, visited, i + 1, j, numRows, numCols);
        numIslandsHelper(grid, visited, i, j - 1, numRows, numCols);
        numIslandsHelper(grid, visited, i, j + 1, numRows, numCols);
    }
}

A BFS Solution:
public class Solution {
    private Queue<Integer> queue = new LinkedList();
    
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return 0;
        }
        int rows = grid.length;
        int cols = grid[0].length;
        boolean[][] visited = new boolean[rows][cols];
        
        int result = 0;
        
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (grid[i][j] == '1' && !visited[i][j]) {
                    result++;
                    numIslandsHelper(grid, visited, i, j, rows, cols);
                }
            }
        }
        return result;
    }
    
    private void numIslandsHelper(char[][] grid, boolean[][] visited, int i, int j, int numRows, int numCols) {
        fill(grid, visited, i, j, numRows, numCols);
        
        while (!queue.isEmpty()) {      
            int cord = queue.poll();
            int x = cord / numCols;
            int y = cord % numCols;
    
            fill(grid, visited, x - 1, y, numRows, numCols);
            fill(grid, visited, x + 1, y, numRows, numCols);
            fill(grid, visited, x, y - 1, numRows, numCols);
            fill(grid, visited, x, y + 1, numRows, numCols);
        }
    }
    
    private void fill(char[][] grid, boolean[][] visited, int i, int j, int numRows, int numCols) {
        if (i < 0 || i >= numRows || j < 0 || j >= numCols) {
            return;
        }
        
        if (visited[i][j] || grid[i][j] == '0') {
            return;
        }
        
        visited[i][j] = true;
        
        queue.offer(i * numCols + j);
    }
}

Update on 1/25/16:
Union-find Solution:
public class Solution {
    private int[][] dir = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }
        
        int m = grid.length;
        int n = grid[0].length;
        
        int[] parents = new int[m * n];
        int count = 0;
        
        // Step 1: initialize each node, for each's parent is itself
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int id = i * n + j;
                parents[id] = id;
            }
        }
        
        // step 2: iterate each node, and connect the neighbors
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == '0') {
                    continue;
                }
                
                for (int[] row : dir) {
                    int x = i + row[0];
                    int y = j + row[1];
                    if (isValid(grid, x, y)) {
                        int p = i * n + j;
                        int q = x * n + y;
                        connect(parents, p, q);
                    }
                }
            }
        }
        
        // step 3: count the number of cc
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int id = i * n + j;
                if (grid[i][j] == '1' && parents[id] == id) {
                    count++;
                }
            }
        }
        
        return count;
    }
    
    private boolean isValid(char[][] grid, int x, int y) {
        int m = grid.length;
        int n = grid[0].length;
        
        return x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1';
    }
    
    private void connect(int[] parents, int p, int q) {
        int pRoot = find(parents, p);
        int qRoot = find(parents, q);
        
        if (pRoot == qRoot) {
            return;
        }
        
        parents[pRoot] = qRoot;
    }
    
    private int find(int[] parents, int id) {
        while (parents[id] != id) {
            id = parents[id];
        }
        
        return id;
    }
}

Updata on 4/21/19:
public class Solution {
    /**
     * @param grid: a boolean 2D matrix
     * @return: an integer
     */
    public int numIslands(boolean[][] grid) {
        // write your code here
        if (grid == null || grid.length == 0) {
            return 0;
        }
        
        int nRows = grid.length;
        int nCols = grid[0].length;
        
        int[] parents = new int[nRows * nCols];
        for (int i = 0; i < parents.length; i++) {
            parents[i] = i;
        }
        
        for (int i = 0; i < nRows; i++) {
            for (int j = 0; j < nCols; j++) {
                if (grid[i][j]) {
                    union(grid, i, j, parents);
                }
            }
        }
        
        int numIslands = 0;
        
        for (int i = 0; i < parents.length; i++) {
            int nx = i / nCols;
            int ny = i % nCols;
            
            if (grid[nx][ny] && parents[i] == i) {
                numIslands++;
            }
        }
        
        return numIslands;
    }
    
    private void union(boolean[][] grid, int x, int y, int[] parents) {
        int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        
        int nRows = grid.length;
        int nCols = grid[0].length;
        
        for (int i = 0; i < 4; i++) {
            int nx = x + dirs[i][0];
            int ny = y + dirs[i][1];
            
            if (nx >= 0 && nx < nRows && ny >= 0 && ny < nCols && grid[nx][ny]) {
                int myParrent = find(parents, x * nCols + y);
                int neighborParrent = find(parents, nx * nCols + ny);
                
                if (myParrent != neighborParrent) {
                    parents[myParrent] = neighborParrent;
                }
            }
        }
    }
    
    private int find(int[] parents, int x) {
        int root = x;
        while (parents[root] != root) {
            root = parents[root];
        }
        
        // path compression
        //
        while (x != root) {
            int temp = parents[x];
            parents[x] = root;
            x = temp;
        }
        
        return root;
    }
}

Leetcode: Ugly Number

Write a program to check whether a given number is an ugly number.
Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. For example, 6, 8 are ugly while 14 is not ugly since it includes another prime factor 7.
Note that 1 is typically treated as an ugly number.
Credits:
Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.
Understand the problem:
This problem can be solved by using greedy algorithm. First check if it could be divided by 2s, if not then 3s, and then 5s. Finally check if the number is 1. If yes, return true else return false. 

Code (Java):
public class Solution {
    public boolean isUgly(int num) {
        if (num <= 0) {
            return false;
        }
        
        while (num > 1 && num % 2 == 0) {
            num /= 2;
        }
        
        while (num > 1 && num % 3 == 0) {
            num /= 3;
        }
        
        while (num > 1 && num % 5 == 0) {
            num /= 5;
        }
        
        if (num == 1) {
            return true;
        } else {
            return false;
        }
    }
}

Monday, April 27, 2015

Giraph: Page Rank

Problem Description:
Assign a weight to each node of a graph which represents its relative importance inside the graph. PageRank usually refers to a set of webpages, and tries to measure which ones are the most important in comparison with the rest from the set. The importance of a webpage is measured by the number of incoming links, i.e. references it receives from other webpages.
Here is a very good reference:
http://marsty5.com/2013/05/29/run-example-in-giraph-pagerank/

Code (Giraph):
package org.apache.giraph.examples;

import org.apache.giraph.graph.BasicComputation;
import org.apache.giraph.conf.LongConfOption;
import org.apache.giraph.edge.Edge;
import org.apache.giraph.graph.Vertex;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.FloatWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.log4j.Logger;

import java.io.IOException;

/**
 * My simplified Google page rank example.
 */
@Algorithm(
    name = "Page Rank",
    description = "My simplified page rank"
)

public class MyPageRankComputation extends BasicComputation<
    LongWritable, DoubleWritable, FloatWritable, DoubleWritable> {

  public static final int MAX_SUPERSTEPS = 2;
  
  @Override
  public void compute(Vertex<LongWritable, DoubleWritable, FloatWritable> vertex, 
      Iterable<DoubleWritable> messages) throws IOException {
  
    if (getSuperstep() >= 1) {
      double sum = 0;
      for (DoubleWritable message : messages) {
        sum += message.get();
      }
      vertex.setValue(new DoubleWritable(sum));
    }

    if (getSuperstep() < MAX_SUPERSTEPS) {
      int numEdges = vertex.getNumEdges();
      DoubleWritable message = new DoubleWritable(vertex.getValue().get() / numEdges);
      for (Edge<LongWritable, FloatWritable> edge: vertex.getEdges()) {
        sendMessage(edge.getTargetVertexId(), message);
      }
      //sendMessageToAllEdges(vertex, message);
    }
    vertex.voteToHalt();
  }  
}

Advanced features of the Giraph: Master compute and aggregators
Some useful link:
http://giraph.apache.org/aggregators.html

According to the Google Pregel's paper: aggregators are a mechanism for global communication, monitoring and data. Each vertex can provide a value to an aggregator in superstep S, the system combines those values using a reduction operator, and the resulting value is made available to all vertices in superstep S + 1. 


What are aggregators?

Aggregators enable global computation in your application. You can use them, for example, to check whether a global condition is satisfied or to keep some statistics.
During a superstep, vertices provide values to aggregators. These values get aggregated by the system and the results become available to all vertices in the following superstep. Providing a value to aggregator is done by calling:
aggregate(aggregatorName, aggregatedValue)
and to get the value aggregated during previous superstep you should call:
getAggregatedValue(aggregatorName)
Aggregator operations should be commutative and associative since there is no guaranteed ordering in which the aggregations will be performed.

Regular vs persistent aggregator

Aggregators come in two flavors: regular and persistent aggregators. The value of a regular aggregator will be reset to the initial value in each superstep, whereas the value of persistent aggregator will live through the application.
As an example, consider LongSumAggregator being used by each vertex adding 1 to it during compute(). If this is a regular aggregator, you'll be able to read the number of vertices in the previous superstep from it. If this is persistent aggregator, it will hold the sum of the number of vertices from all of the previous supersteps.

Registering aggregators

Before using any aggregator, you MUST register it on the master. You can do this by extending (and setting) MasterCompute class, and calling:
registerAggregator(aggregatorName, aggregatorClass)
or:
registerPersistentAggregator(aggregatorName, aggregatorClass)
depending on what kind of aggregator you want to have. You can register aggregators either in MasterCompute.initialize() - in that case the registered aggregator will be available through whole application, or you can do it in MasterCompute.compute() - the aggregator will then be available in current and each of the following supersteps.

Aggregators and MasterCompute

The first thing that gets executed in a superstep is MasterCompute.compute(). In this method you are able to read aggregated values from previous superstep by calling:
getAggregatedValue(aggregatorName)
and you are able to change the value of the aggregator by calling:
setAggregatedValue(aggregatorName, aggregatedValue)

Implementations

In the package org.apache.giraph.aggregators you can find many common aggregators already implemented.
If you need to create your own, you can extend BasicAggregator or implement Aggregator. Methods which you will need to implement are:
aggregate(value)
which describes how to add another value to the aggregator, and:
createInitialValue()
The initial value will be applied to all aggregator objects when they are created. When it's added to an aggregator the value of the aggregator shouldn't change. For example, for sum aggregator this value should be zero, for min aggregator it should be the value corresponding to positive infinity, etc.
If you need some parameters from the configuration in your aggregators, your aggregator class can implement ImmutableClassesGiraphConfigurable.

Advanced options

If you are using multiple threads for computation (giraph.numComputeThreads), you should consider turning on giraph.useThreadLocalAggregators option. Using thread local aggregators allows every worker thread to have it's own local aggregator copy, rather than a single aggregator copy for the entire worker. The downside of this approach is that it will use more memory - you'll have several copies of each of the aggregators. So if you have a lot of aggregators, or aggregated values are very large objects, this option could be bad. But otherwise, it will likely speed up your application since it will remove the need to perform synchronization when aggregating values.

Implementation details

During a superstep, values provided to the aggregators are being aggregated to some worker-local aggregator objects. In the end of the superstep, all of these values need to be aggregated together, given to the master, and after MasterCompute.compute is performed, distributed back to all the workers. In applications which use a lot of aggregators, if all the aggregations were to be done on master, this could cause a serious bottleneck, both from the computation and network communication standpoint, because master would be receiving, processing and sending (number of workers * total size of aggregators) amount of data. This was the motivation for implementing sharded aggregators in Giraph.
In the end of the superstep, each aggregator is assigned to one of the workers, and that worker receives and aggregates values for that aggregator from all other workers. Then worker sends all its aggregators to master, so master doesn't have to perform any aggregation, and receives only final value for each of the aggregators. After MasterCompute.compute, master doesn't do the distribution of all aggregators to all workers, but aggregators again have their owners. Master only sends each aggregator to its owner, and then each worker distributes the aggregators which it owns to all other workers.

Code (Java):
package org.apache.giraph.examples;

import org.apache.giraph.aggregators.DoubleMaxAggregator;
import org.apache.giraph.aggregators.DoubleMinAggregator;
import org.apache.giraph.aggregators.LongSumAggregator;
import org.apache.giraph.edge.Edge;
import org.apache.giraph.edge.EdgeFactory;
import org.apache.giraph.graph.BasicComputation;
import org.apache.giraph.graph.Vertex;
import org.apache.giraph.io.VertexReader;
import org.apache.giraph.io.formats.GeneratedVertexInputFormat;
import org.apache.giraph.io.formats.TextVertexOutputFormat;
import org.apache.giraph.master.DefaultMasterCompute;
import org.apache.giraph.worker.WorkerContext;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.FloatWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.log4j.Logger;

import com.google.common.collect.Lists;

import java.io.IOException;
import java.util.List;
public class MySimplePageRankComputationWithAggregator extends BasicComputation<LongWritable,
    DoubleWritable, FloatWritable, DoubleWritable> {
  /** Number of supersteps for this test */
  public static final int MAX_SUPERSTEPS = 30;
  /** Logger */
  private static final Logger LOG = 
      Logger.getLogger(MySimplePageRankComputationWithAggregator.class);
  /** Sum aggregator name */
  private static String SUM_AGG = "sum";
  /** Min aggregator name */
  private static String MIN_AGG = "min";
  /** Max aggregator name */
  private static String MAX_AGG = "max";

  @Override
  public void compute(
      Vertex<LongWritable, DoubleWritable, FloatWritable> vertex, 
      Iterable<DoubleWritable> messages) throws IOException {
    if (getSuperstep() > 0) {
      double sum = 0;
      for (DoubleWritable message : messages) {
        sum += message.get();
      }
      DoubleWritable vertexValue = 
          new DoubleWritable(0.15f / getTotalNumVertices() + 0.85f * sum);
      vertex.setValue(vertexValue);
      aggregate(MAX_AGG, vertexValue);
      aggregate(MIN_AGG, vertexValue);
      aggregate(SUM_AGG, new LongWritable(1));
      LOG.info(vertex.getId() + ": PageRank=" + vertexValue + 
          " max=" + getAggregatedValue(MAX_AGG) + 
          " min=" + getAggregatedValue(MIN_AGG));
    }

    if (getSuperstep() < MAX_SUPERSTEPS) {
      long edges = vertex.getNumEdges();
      sendMessageToAllEdges(vertex, 
          new DoubleWritable(vertex.getValue().get() / edges));
    } else {
      vertex.voteToHalt();
    }
  }

  /**
   * Master compte 
   * It registers required aggregators
   */
  public static class MySimplePageRankMasterCompute extends 
      DefaultMasterCompute {
    @Override
    public void initialize() throws InstantiationException,
        IllegalAccessException {
      registerAggregator(SUM_AGG, LongSumAggregator.class);
      registerPersistentAggregator(MIN_AGG, DoubleMinAggregator.class);
      registerPersistentAggregator(MAX_AGG, DoubleMaxAggregator.class);
    }
  }
}

Input:
[0,1,[[1,1],[3,3]]]
[1,2,[[0,1],[2,2],[3,1]]]
[2,3,[[1,2],[4,4]]]
[3,4,[[0,3],[1,1],[4,4]]]

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

Expected output: 
0       0.18589980877086507
2       0.19005494651531296
1       0.2704106097936198
3       0.2703977512806641

4       0.19006780502826862


Thursday, April 23, 2015

Giraph: Single Source Shortest Path (SSSP)

Problem description:
Given a directed graph, find the shortest path from a given source vertex to all connected vertices. Use Dijkstra algorithm suppose all edges are non-negative. 

Example Input:
[0,0,[[1,1],[3,3]]]
[1,0,[[0,1],[2,2],[3,1]]]
[2,0,[[1,2],[4,4]]]
[3,0,[[0,3],[1,1],[4,4]]]
[4,0,[[3,4],[2,4]]]
Each line above has the format [source_id,source_value,[[dest_id, edge_value],...]]. In this graph, there are 5 nodes and 12 directed edge. 

Output:
0       1.0
2       2.0
1       0.0
3       1.0
4       5.0

Understand the problem:
In this problem, we assume the vertex value is the shortest distance from the source vertex to the current vertex. Initially, each vertex value is initialized to MAX. 

In each superstep,  each vertex first receives messages from its neighbors, updated the potential minimum distance from the source vertex. If the minimum value is less than the current vertex value, then the vertex updates its value and send updates to its immediate neighbors. These neighbors in turn will update their values and send messages, resulting in a wavefront of updates through the graph. 

The algorithm terminates when no updates occur, when no messages sent through the graph. Thus all vertexes denotes its status to inactive. Finally the value associated with each vertex is the shortest path from the source vertex. Termination is guaranteed if all edge weights are non-negative, as required by the Dijkstra algorithm.  

Code (Java):
package org.apache.giraph.examples;

import org.apache.giraph.graph.BasicComputation;
import org.apache.giraph.conf.LongConfOption;
import org.apache.giraph.edge.Edge;
import org.apache.giraph.graph.Vertex;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.FloatWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.log4j.Logger;

import java.io.IOException;

/**
 * Demonstrates the basic Pregel shortest paths implementation.
 */
@Algorithm(
    name = "Shortest paths",
    description = "Finds all shortest paths from a selected vertex"
)
/**
 *  @param LongWritable I  Vertex Id
 *  @param DoubleWritable V Vertex data
 *  @param FloatWritable E  Edge data
 *  @param DoubleWritable M Message data
 */
public class SimpleShortestPathsComputation extends BasicComputation<
    LongWritable, DoubleWritable, FloatWritable, DoubleWritable> {
  /** The shortest paths id */
  /**
   * @param String  key 
   * @param long    defaultValue
   * @param String  description
   */
  public static final LongConfOption SOURCE_ID =
      new LongConfOption("SimpleShortestPathsVertex.sourceId", 1,
          "The shortest paths id");

  /** Class logger */
  private static final Logger LOG =
      Logger.getLogger(SimpleShortestPathsComputation.class);

  /**
   * Is this vertex the source id?
   *
   * @param vertex Vertex
   * @return True if the source id
   */
  /**
   * Vertex class
   * @param LongWritavle I Vertex Id
   * @param ?   V          Vertex data
   * @param ?   E          Edge data
   */
  private boolean isSource(Vertex<LongWritable, ?, ?> vertex) {
    return vertex.getId().get() == SOURCE_ID.get(getConf());
  }

  @Override
  /**
   * compte(Vertex<I,V,E> vertex, Iterable<M> messages)
   */
  public void compute(
      Vertex<LongWritable, DoubleWritable, FloatWritable> vertex,
      Iterable<DoubleWritable> messages) throws IOException {
    if (getSuperstep() == 0) {
      vertex.setValue(new DoubleWritable(Double.MAX_VALUE));
    }
    double minDist = isSource(vertex) ? 0d : Double.MAX_VALUE;
    for (DoubleWritable message : messages) {
      minDist = Math.min(minDist, message.get());
    }
    if (LOG.isDebugEnabled()) {
      LOG.debug("Vertex " + vertex.getId() + " got minDist = " + minDist +
          " vertex value = " + vertex.getValue());
    }
    if (minDist < vertex.getValue().get()) {
      vertex.setValue(new DoubleWritable(minDist));
      /**
       * Edge class
       * @param LongWritable I Target vertex idex
       * @param FloatWritable E Edge value
       */
      for (Edge<LongWritable, FloatWritable> edge : vertex.getEdges()) {
        double distance = minDist + edge.getValue().get();
        if (LOG.isDebugEnabled()) {
          LOG.debug("Vertex " + vertex.getId() + " sent to " +
              edge.getTargetVertexId() + " = " + distance);
        }
        sendMessage(edge.getTargetVertexId(), new DoubleWritable(distance));
      }
    }
    vertex.voteToHalt();
  }
}


Discussion:
We could animate the process of the program execution using the example input for each superstep. 
superstep 0, 
since minDist in Vertex 1 is 0 while the rests are MAX, only the source vertex updates its  value to 0 and sends distance to its neighbors
Each vertex updates its value:
V   vertexVal           
0    INF                
1    INF -> 0            
2    INF                
3    INF                   
4    INF         

Each vertex sends messages to its neighbors:
V    (targetVertexId, distance)
0       -            - 
1    <(0,1) (2,2) (3,1)>    
2       - 
3       - 
4       -

superstep 1, 
Each vertex receives incoming messages from the last superstep:
V   message
0    1 (from Vertex 1)
1    -
2    2 (from Vertex 1)
3    1 (from Vertex 1)
4    -

Each vertex updates its value:
V   vertexVal           
0    INF -> 1            
1     0                  
2    INF -> 2               
3    INF -> 1                 
4    INF              

Each vertex sends messages to its neighbors: 
V    (targetVertexId, distance)
0    (1,2) (3,4)
1      -
2    (1,4) (4,6)  
3    (0,4) (1,2) (4,5) 
4       -

superstep 2: 
Each vertex receives incoming messages from the last superstep:
V    messages
0     4 (from vertex 3)
1     2 (from vertex 0), 4 (from vertex 2), 2 (from vertex 3)
2     -
3     4 (from vertex 0) 
4     6 (from vertex 2), 5 (vertex 3)

Each vertex updates its value:
V    vertexVal
0      1 (discard 4)
1      0 (discard 2, 4, 2)
2      2  (no messages)
3      1  (discard 4)
4     INF -> 5

Each vertex sends messages to its neighbors: 
V    (targetVertexId, distance)
0    -
1    -
2    -
3    -
4    (2,9) (3,9)

superstep 3:
Each vertex receives incoming messages from the last superstep:
V    messages
0    -
1    -
2    9 (from vertex 4)
3    9 (from vertex 4)
4    -

Each vertex updates its value:
V    vertexVal
0    1
1    0
2    2 (discard 9)
3    1 (discard 9)
4    5

Send messages to its neighbors:
V    (targetVertexId, distance)
0     -
1     -
2     -
3     -
4     -

superstep 4:
Terminates since no messages received for all vertexes, all vertexes become inactive. 
Final vertex values:
V    vertexVal
0      1
1      0
2      2
3      1
4      5
Note that the order of the output is not determined. 

Summary:
1. Giraph is based on BSP model, so make sure you understand the running process in each super step. 
2. Some useful APIs:
Vertex class:
/**
 * Vertex class
 * @param I    Vertex Id
 * @param V    Vertex data
 * @param E    Edge data
 */
Interface Vertex<I extends org.apache.hadoop.io.WritableComparable,
    V extends org.apache.hadoop.io.Writable,
    E extends org.apache.hadoop.io.Writable>


// Get a read-only view of the out-edges of this vertex.
Iterable<Edge<I,E>> getEdges();

/** 
 * Return the value of the first edge with the given target vertex id, 
 * or null if there is no such edge. 
 */ 
E getEdgeValue(I targetVertexId);

// Get the vertex id.
I getId();

// Get the vertex value (data stored with vertex)
V getValue();

// Get the number of outgoing edges on this vertex. 
int getNumEdges();

// Set the outgoing edges for this vertex.
void setEdges(Iterable<Edge<I,E>> edges)

// If an edge to the target vertex exists, set it to the given edge value.
void setEdgeValue(I targetVertexId, E edgeValue)

/** Set the vertex data (immediately visible in the computation) */
void setValue(V value)

/** After this is called, the compute() code will no longer be called 
  * for this vertex unless a message is sent to it. 
  */
void voteToHalt()

/** 
  * Is this vertex done? 
  */
boolean isHalted()

Edge class:
/**
 * Edge class
 * @param I Vertex index
 * @param E Edge value 
 */
Interface Edge<I extends org.apache.hadoop.io.WritableComparable,
    E extends org.apache.hadoop.io.Writable>

// Get the target vertex index of this edge
I getTargetVertexId()

// Get the edge value of the edge
E getValue()

BasicComputation class:
/**
 * BasicComputation Class
 * @param I Vertex id
 * @param V Vertex data
 * @param E Edge data
 * @param M Message data
 */
Class BasicComputation<I extends org.apache.hadoop.io.WritableComparable,
    V extends org.apache.hadoop.io.Writable,
    E extends org.apache.hadoop.io.Writable,
    M extends org.apache.hadoop.io.Writable>
    
// Must be defined by user to do computation on a single Vertex.
public abstract void compute(Vertex<I,V,E> vertex,
           Iterable<M1> messages)
           throws IOException
           
// Retrive the current superstep, starting from 0
public long getSuperstep();

Thursday, April 2, 2015

Java: Chapter 4: Everything is an object

Chapter 4: Everything is an object
  1. Java is not "pass-by-reference", Java passes on object reference by values.
  2. Java put objects created by using new into heap.
  3. Primitive data types are in the stack.
  4. Wrapper class: allows to make non-primitive object on the heap to represent that primitive type. 
    1. Why wrapper primitives? Shown in later chapter. 
  5. Autoboxing: automatically converts from primitive types to wrapper class, and verse vera. 
  6. BigInteger, BigDecimal: arbitrary-precision integers or fixed-point numbers. Used to more accurate calculations. 
  7. Array in Java is guaranteed to be initialized and cannot be accessed outside of its range. The range checking comes at price of a small amount of memory overhead on each array, as well as verifying the index at run time. But the assumption is that the safety and increased productivity are worth the expense. 
  8. Garbage collector looks at all the objects that were created with new and figures out which ones are not being referenced anymore. Then it releases the memory for those objects, so the memory can be used for new objects. 
  9. A Java class consists of fields and methods. Fields are sometimes called data members. Methods are sometimes called member functions. 
  10. Primitive type as a data member will be always initialized. However, local variables are not automatically initialized. If you do so, you will get a compile-time error. 
  11. Signature: method name and the argument list are called signature of a method because it could uniquely identify that method. 
  12. static keyword in Java:
    1. There are two situations in which the static is needed. 
      1. Static data members: if you wanna have only a single piece of storage for a particular field, regardless of how many objects of that class that created, or even if no objects are created. 
      2. Static methods: if you need a method that isn't associated with any particular object of this class. That is, you need a method that you can call even if no objects are crated. 
    2. static filed (data members): 
class StaticTest {
    static int i = 47;
}
// Access to the data members can be either of the two ways:
StaticTest st1 = new StaticTest();
st1.i++; // OR
StaticTest.i++; // The second way is more preferred.