Phone Screen @Box

Position: Software Engineer, Full Stack/Developer Experience

  1. Introduce what they are working on
  2. Technical Questions
    1. Merge two sorted array: just describe the algorithm and time complexity
    2. Merge K sorted array: write the code.
    3. Two Sum: an sorted array, find if there is at least a pair of two elements which the sum equals 0. Just describe the algorithm and time complexity.
      I gave him two approaches: using HashMap and two pointers.
      Then he asked me to write the code with two pointers.
    4. Three Sum: just describe the algorithm and time complexity.
FacebookTwitterGoogle+Share

Elevator Design

How would I design the elevators for a new 40 story office

building that had an average of 100 people per floor to most efficiently fill and empty the building given a standard 9-5 workday and traffic conditions in my city? The answer needed to be completely detailed, including expected passengers per car, time per stop, average floors stops per trip at various hours, etc.

1. Ask

  1. How many elevators are there?
  2. What is the capacity of each elevator?
  3. Is the efficiency goal focused only at the start & end of day & not in between (i.e. lunch time, breaks)?

2. General optimal critiera

  • provide even service to each floor
  • minimize how long passengers wait for an elevator to arrive
  • minimize how long passengers spend to get to their destination floor
  • serve as many passengers as possible

The first advice that I’ve read is to ask some questions before you start answering. It will show that you are strategic & don’t jump to random assumptions. So I will probably ask questions like: Is the efficiency goal focused only at the start & end of day & not in between (i.e. lunch time, breaks)? How many elevators are there? What is the capacity of each elevator?

2) Assuming that everything is average, i.e. 6 elevators, 15 people per elevator, and focus only on start and end date, then the sample data should follow a normal distribution.

730-8 – 2%

8-830 – 14%

830-9 – 34%

9-930 – 34%

930-10 – 14%

10-1030 – 2%

3) I will break this down & solve the worst case scenario first. This means, 34 people x 40 floors = 1360 people to be transported by 6 elevator x 15 = total 90 capacity during 830-9 or 9-930 am.

4) Focusing on this more manageable problem, 1360 / 90 means each elevator will make 15 full cycles (lobby to highest floor and back) 5) Since we want to minimize the cycle time for each elevator, we assign one elevator per subset of 40/6 consecutive floors. This should address the issue on minimizing time per stop. 6) That means, the final design should be a load balancing of the elevators by minimizing the travel time — Elevator A – 1st to 7th floor, B – 8th to 14th floor, and so forth. Do you guys see anything wrong with this line of thinking?

先说说我对单个电梯设计的想法(欢迎批评指正)

1 Elevator Object, 应该包含physical components: Door, Indicator Lights,
Control Panel. 一些性质(Non physical properties): Speed, Num of floors,
capacity, max weight. 所能从事的操作methods: moveto, stop, ringbell。然后电
梯应该能够handle user request, 所以还应有一个requestQueue, 电梯应该根据自己
的state 和 requestQueue做出moveto, stop的决定,所以有一component:
requestHandler(Strategy pattern),可以set不同的requestHanlder.

2 Door, properties: State, method: open, close, getState.

3 Indicator light(指示所到楼层),properties: state; method: on, off,
getState

4 Control Panel, 包含physical component: Floor Buttons, Other buttons(也可直
接把Buttons 当作 elevator的components,还没考虑哪一个方法好)

5 Button, properties: floorNum, Parent Elevator, methods: OnPress(Observer
Pattern).

6 ElevatorRequestHandler: handleRequest(Elevator ele, requestList rlist), 可
以define 一个interface, 然后又各种不同实现

7 Request: 可以define 一个abstract class, 然后有子类movingRequest,
helpRequest doorRequest etc.

A Single Elevator

Use Case:

  1. User
    1. press a button to summon the lift
    2. press a button to get to a specific floor
  2. Button
    1. floor button and level button
    2. illuminates when pressed
    3. place an ‘elevator request’ when pressed
  3. Elevator
    1. moves up/down
    2. open/close the door

ElevatorRequests Class

Each button press results in an elevator request which has to be served. Each of these requests is tracked at a global place. ElevatorRequests, the class which stores elevator requests can use different strategies to schedule the elevator requests.

ElevatorController

The elevator is controlled by a controller class which we call ElevatorController. The elevator controller instructs the elevator what to do and also can shutdown/start up the elevator of the building. The elevator controller reads the next elevator request to be processed and serves it.

Button (Abstract) Class

Button is abstract class defining common behavior like illuminate, doNotIlluminate. FloorButton, ElevatorButton extend Button type and define placeRequest() which is invoked when the button is pressed.

In conclusion, ElevatorController runs the show by reading the ElevatorRequests to process and instructing the Elevator what to do. User send request by pressing Buttons.

Extend the answer to multiple elevators

  1. Each elevator have 1 controller.
  2. Floor based requests can be served by any elevator, thus these requests are added to a common area accessible by all controllers.
  3. Each elevator controller runs as a separate thread and checks if it can process a floor request. Mind synchronization issues.

http://www.columbia.edu/~cs2035/courses/ieor4405.S13/p14.pdf

https://github.com/joeblau/sample-elevator-control-system

http://blog.jobbole.com/74672/

http://blog.gssxgss.me/elevator-simulator-java/

Count Univalue Subtrees

Count Univalue Subtrees

Given a binary tree, count the number of uni-value subtrees.

A Uni-value subtree means all nodes of the subtree have the same value.

For example:
Given binary tree,

              5
             / \
            1   5
           / \   \
          5   5   5

return 4.

Solution:

public int countUnivalSubtrees(TreeNode root) {
    int[] count = new int[1];
    isUnivalSubtrees(root, count);
    return count[0];
}

public boolean isUnivalSubtrees(TreeNode root, int[] count) {
    if (root == null) {
        return false;
    }
    boolean left = isUnivalSubtrees(root.left, count);
    boolean right = isUnivalSubtrees(root.right, count);
    if (!left && !right) {
        if (root.left == null && root.right == null) {
            count[0]++;
            return true;
        }
    } else if (left && right) {
        if (root.left.val == root.val && root.right.val == root.val) {
            count[0]++;
            return true;
        }
    } else if (left && !right) {
        if (root.right == null && root.left.val == root.val) {
            count[0]++;
            return true;
        }
    } else if (!left && right) {
        if (root.left == null && root.right.val == root.val) {
            count[0]++;
            return true;
        }
    }
    return false;
}

Ugly Number I & II

Ugly Number I

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.

public boolean isUgly(int num) {
        if (num <= 0) {
            return false;
        }
        if (num == 1) {
            return true;
        }
        int[] factors = {2, 3, 5};
        for (int i = 0; i < factors.length; i++) {
            while (num % factors[i] == 0) {
                num = num / factors[i];
            }
        }
        return num == 1;
    }

Ugly Number II

Write a program to find the n-th ugly number.

Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. For example, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.

Note that 1 is typically treated as an ugly number.

https://leetcode.com/discuss/52716/o-n-java-solution

Solution:

O(n) time, one pass solution. O(n) space.

The idea of this solution is from this page:http://www.geeksforgeeks.org/ugly-numbers/

The ugly-number sequence is 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, … because every number can only be divided by 2, 3, 5, one way to look at the sequence is to split the sequence to three groups as below:

(1) 1×2, 2×2, 3×2, 4×2, 5×2, …
(2) 1×3, 2×3, 3×3, 4×3, 5×3, …
(3) 1×5, 2×5, 3×5, 4×5, 5×5, …

We can find that every subsequence is the ugly-sequence itself (1, 2, 3, 4, 5, …) multiply 2, 3, 5.

Then we use similar merge method as merge sort, to get every ugly number from the three subsequence.

Every step we choose the smallest one, and move one step after,including nums with same value.

    public int nthUglyNumber(int n) {
        int[] ugly = new int[n];
        ugly[0] = 1;
        int index2 = 0, index3 = 0, index5 = 0;
        int factor2 = 2, factor3 = 3, factor5 = 5;
        for (int i = 1; i < n; i++) {
            int min = Math.min(Math.min(factor2, factor3), factor5);
            ugly[i] = min;
            if (factor2 == min) {
                index2++;
                factor2 = 2 * ugly[index2];
            }
            if (factor3 == min) {
                index3++;
                factor3 = 3 * ugly[index3];
            }
            if (factor5 == min) {
                index5++;
                factor5 = 5 * ugly[index5];
            }
        }
        return ugly[n - 1];
    }

Union-Find 并查集

Union-Find is an algorithm which is widely used to solve finding disjoint components.

void init(int[] nums) {
    int[] fathers = new int[nums.length];
    //initially, father is itself
    for (int i = 0; i < nums; i++) {
        fathers[i] = i;
    }
}

//find the father for both nodes
//if they are in different groups, union them by setting fathers[xFather] = yFather;
void union(int x, int y, int[] fathers) {
    int xFather = findAndSet(x, fathers);
    int yFather = findAndSet(y, fathers);
    fathers[xFather] = yFather;
}

//recursively find its parent until reach the top which its parent is itself
//to improve the performance, we compress the path by setting the top father
//so that next time we call find, it is O(1) time complexity
int findAndSet(int x, int[] fathers) {
    int parent = fathers[x];
    while (parent != fathers[parent]) {
        parent = fathers[parent];
    }
    return parent;
}

Related Questions:

1. Graph Valid Tree Medium

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.

    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 in edges.

    //union-find algorithm
    //findSet is a faster version of find method which finds the top father of a node
    //and then set the father to
    //which basically compress the path.
    //another improvement could be: only
    public boolean validTree(int n, int[][] edges) {
        int[] fathers = new int[n];
        for (int i = 0; i < n; i++) {
            fathers[i] = i;
        }
        for (int i = 0; i < edges.length; i++) {
            //union
            int xfather = findSet(edges[i][0], fathers);
            int yfather = findSet(edges[i][1], fathers);
            //since there is no duplicate edge(important! otherwise this algorithm is wrong)
            //so if xfather == yfather means they are in
            if (xfather == yfather) {
                return false;
            }
            fathers[xfather] = yfather;
        }
        //there should only be one node(root) pointing to itself
        int count = 0;
        for (int i = 0; i < fathers.length; i++) {
            if (fathers[i] == i) {
                count++;
            }
        }
        return count == 1;
    }
    
    public int findSet(int x, int[] fathers) {
        int parent = fathers[x];
        while (parent != fathers[parent]) {
            parent = fathers[parent];
        }
        fathers[x] = parent;
        return parent;
    }

    2. Find the Connected Component in the Undirected Graph

    public List<List<Integer>> connectedSet(ArrayList<UndirectedGraphNode> nodes) {
        //init unions
        Map<Integer, Integer> fathers = new HashMap<>();
        for (UndirectedGraphNode node : nodes) {
            int label = node.label;
            if (!fathers.containsKey(label)) {
                fathers.put(label, label);
            }
            for (UndirectedGraphNode neighbor : node.neighbors) {
                union(label, neighbor.label, fathers);
            }
        }
        //put it into a new map which key is the father of the component
        //value is a list of node within the component
        Map<Integer, List<Integer>> components = new HashMap<>();
        for (Integer label : fathers.keySet()) {
            int father = findAndSet(label, fathers);
            if (components.containsKey(father)) {
                components.get(father).add(label);
            } else {
                List<Integer> component = new ArrayList<>();
                component.add(label);
                components.put(father, component);
            }
        }
        //sort the result
        List<List<Integer>> res = new ArrayList<>();
        for (List<Integer> component : components.values()) {
            Collections.sort(component);
            res.add(component);
        }
        return res;
    }
    
    public void union(int x, int y, Map<Integer, Integer> fathers) {
        int xFather = findAndSet(x, fathers);
        int yFather = findAndSet(y, fathers);
        if (xFather != yFather) {
            fathers.put(xFather, yFather);
        }
    }
    
    public int findAndSet(int label, Map<Integer, Integer> fathers) {
        if (!fathers.containsKey(label)) {
            fathers.put(label, label);
            return label;
        }
        int parent = fathers.get(label);
        while (parent != fathers.get(parent)) {
            parent = fathers.get(parent);
        }
        fathers.put(label, parent);
        return parent;
    }

    3. Find the Weak Connected Component in the Directed Graph

    public List<List<Integer>> connectedSet2(ArrayList<DirectedGraphNode> nodes) {
        //init unions
        Map<Integer, Integer> fathers = new HashMap<>();
        for (DirectedGraphNode node : nodes) {
            int label = node.label;
            if (!fathers.containsKey(label)) {
                fathers.put(label, label);
            }
            for (DirectedGraphNode neighbor : node.neighbors) {
                union(label, neighbor.label, fathers);
            }
        }
        //put it into a new map which key is the father of the component
        //value is a list of node within the component
        Map<Integer, List<Integer>> components = new HashMap<>();
        for (Integer label : fathers.keySet()) {
            int father = findAndSet(label, fathers);
            if (components.containsKey(father)) {
                components.get(father).add(label);
            } else {
                List<Integer> component = new ArrayList<>();
                component.add(label);
                components.put(father, component);
            }
        }
        //sort the result
        List<List<Integer>> res = new ArrayList<>();
        for (List<Integer> component : components.values()) {
            Collections.sort(component);
            res.add(component);
        }
    
        return res;
    }
    
    public void union(int x, int y, Map<Integer, Integer> fathers) {
        int xFather = findAndSet(x, fathers);
        int yFather = findAndSet(y, fathers);
        if (xFather != yFather) {
            fathers.put(xFather, yFather);
        }
    }
    
    public int findAndSet(int label, Map<Integer, Integer> fathers) {
        if (!fathers.containsKey(label)) {
            fathers.put(label, label);
            return label;
        }
        int parent = fathers.get(label);
        while (parent != fathers.get(parent)) {
            parent = fathers.get(parent);
        }
        fathers.put(label, parent);
        return parent;
    }

    Partition Array

    1. with target outside the array

        public int partitionArray(int[] nums, int k) {
            if (nums == null || nums.length == 0) {
                return 0;
            }
            int left = 0;
            int right = nums.length - 1;
            while (left <= right) {
                while (left <= right && nums[left] < k) {
                    left++;
                }
                while (left <= right && nums[right] >= k) {
                    right--;
                }
                if (left < right) {
                    swap(nums, left, right);
                    left++;
                    right--;
                }
            }
            return left;
        }
    
        public void swap(int[] nums, int a, int b) {
            int tmp = nums[a];
            nums[a] = nums[b];
            nums[b] = tmp;
        }

    2. with target with in the array(Quick select)