ELEMENTS OF PROGRAMMING w INTERVIEWS Java A ¥ ADNAN AZIZ TSUNG-HSIEN LEE AMIT PRAKASH Elements of Programming Interviews in Java The Insiders' Guide Adnan Aziz Tsung-Hsien Lee Amit Prakash ElementsOfProgramminglnterviews.com Adnan Aziz is a Research Scientist at Facebook Previously, he was a professor at the Department of Electrical and Computer Engineering at The University of Texas at Austin, where he conducted research and taught classes in applied algorithms He received his Ph.D from The University of California at Berkeley; his undergraduate degree is from Indian Institutes of Technology Kanpur He has worked at Google, Qualcomm, IBM, and several software startups When not designing algorithms, he plays with his children, Laila, Imran, and Omar Tsung-Hsien Lee is a Senior Software Engineer at Uber Previously, he worked as a Software Engineer at Google and as Software Engineer Intern at Facebook He received both his M.S and undergraduate degrees from National Tsing Hua University He has a passion for designing and implementing algorithms He likes to apply algorithms to every aspect of his life He takes special pride in helping to organize Google Code Jam 2014 and 2015 Amit Prakash is a co-founder and CTO of ThoughtSpot, a Silicon Valley startup Previously, he was a Member of the Technical Staff at Google, where he worked primarily on machine learning problems that arise in the context of online advertising Before that he worked at Microsoft in the web search team He received his Ph.D from The University of Texas at Austin; his undergraduate degree is from Indian Institutes of Technology Kanpur When he is not improving business intelligence, he indulges in his passion for puzzles, movies, travel, and adventures with Nidhi and Aanya Elements of Programming Interviews: The Insiders' Guide by Adnan Aziz, Tsung-Hsien Lee, and Amit Prakash Copyright © 2015 Adnan Aziz, Tsung-Hsien Lee, and Amit Prakash All rights reserved No part of this publication may be reproduced, stored in a retrieval system, or trans¬ mitted, in any form, or by any means, electronic, mechanical, photocopying, record¬ ing, or otherwise, without the prior consent of the authors The views and opinions expressed in this work are those of the authors and not necessarily reflect the official policy or position of their employers We typeset this book using PTpX and the Memoir class We used TikZ to draw figures Allan Ytac created the cover, based on a design brief we provided The companion website for the book includes contact information and a list of known errors for each version of the book If you come across an error or an improvement, please let us know Version 2.0.0 Website: http://elementsofprogramminginterviews.com To my father, Ishrat Aziz, forgiving me my lifelong love of learning Adnan Aziz To my parents, Hsien-Kuo Lee and Tseng-Hsia Li, for the everlasting support and love they give me Tsung-Hsien Lee To my parents, Manju Shree and Arun Prakash, the most loving parents I can imagine Amit Prakash Table of Contents Introduction I The Interview Getting Ready Strategies For A Great Interview 13 Conducting An Interview 20 Problem Solving 24 II Problems 43 Primitive Types 5.1 Computing the parity of a word 5.2 Swap bits Reverse bits 5.3 5.4 Find a closest integer with the same weight 5.5 Compute x X y without arithmetical operators 5.6 Compute x/y 5.7 Compute xv Reverse digits 5.8 Check if a decimal integer is a palindrome 5.9 5.10 Generate uniform random numbers 5.11 Rectangle intersection 45 48 49 50 51 52 53 54 55 56 57 Arrays 6.1 The Dutch national flag problem Increment an arbitrary-precision integer 6.2 62 65 I 44 60 6.3 6.4 6.5 6.6 6.7 6.8 6.9 6.10 6.11 6.12 6.13 6.14 6.15 6.16 6.17 6.18 6.19 Strings 7.1 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 7.10 7.11 7.12 7.13 Multiply two arbitrary-precision integers Advancing through an array Delete duplicates from a sorted array Buy and sell a stock once Buy and sell a stock twice Enumerate all primes to n Permute the elements of an array Compute the next permutation Sample offline data Sample online data Compute a random permutation Compute a random subset Generate nonuniform random numbers The Sudoku checker problem Compute the spiral ordering of a 2D array Rotate a 2D array Compute rows in Pascal's Triangle 94 Interconvert strings and integers Base conversion Compute the spreadsheet column encoding Replace and remove Test palindromicity Reverse all the words in a sentence Compute all mnemonics for a phone number The look-and-say problem Convert from Roman to decimal Compute all valid IP addresses Write a string sinusoidally Implement run-length encoding Find the first occurrence of a substring Linked Lists Merge two sorted lists Reverse a single sublist Test for cyclicity Test for overlapping lists lists are cycle-free Test for overlapping lists lists may have cycles Delete a node from a singly linked list Remove the kth last element from a list Remove duplicates from a sorted list Implement cyclic right shift for singly linked lists Implement even-odd merge Test whether a singly linked list is palindromic 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 8.10 8.11 66 68 69 70 71 72 74 76 78 79 81 82 83 85 87 90 92 — — ii 95 96 98 98 100 101 102 104 105 106 107 108 109 112 115 116 117 119 121 122 123 124 125 126 127 8.12 8.13 128 129 Implement list pivoting Add list-based integers Stacks and Queues 9.1 Implement a stack with max API 9.2 Evaluate RPN expressions for well-formedness Test a string over 9.3 9.4 Normalize pathnames Search a postings list 9.5 9.6 Compute buildings with a sunset view 9.7 Compute binary tree nodes in order of increasing depth 9.8 Implement a circular queue 9.9 Implement a queue using stacks 9.10 Implement a queue with max API 10 Binary Trees 10.1 Test if a binary tree is height-balanced 10.2 Test if a binary tree is symmetric 10.3 Compute the lowest common ancestor in a binary tree 10.4 Compute the LCA when nodes have parent pointers 10.5 Sum the root-to-leaf paths in a binary tree 10.6 Find a root to leaf path with specified sum 10.7 Implement an inorder traversal without recursion 10.8 Implement a preorder traversal without recursion 10.9 Compute the fcth node in an inorder traversal 10.10 Compute the successor 10.11 Implement an inorder traversal with 0(1) space 10.12 10.13 10.14 10.15 10.16 10.17 Reconstruct a binary tree from traversal data Reconstruct a binary tree from a preorder traversal with markers Form a linked list from the leaves of a binary tree Compute the exterior of a binary tree Compute the right sibling tree Implement locking in a binary tree 131 132 135 137 138 139 140 143 145 146 147 150 152 154 155 157 158 159 160 161 162 163 164 165 167 168 169 171 173 11 Heaps 11.1 Merge sorted files 11.2 Sort an increasing-decreasing array 11.3 Sort an almost-sorted array 11.4 Compute the k closest stars 11.5 Compute the median of online data 11.6 Compute the k largest elements in a max-heap 11.7 Implement a stack API using a heap 175 177 179 180 181 182 184 185 12 Searching 12.1 Search a sorted array for first occurrence of k 187 190 iii 12.2 12.3 12.4 12.5 12.6 12.7 12.8 12.9 12.10 Search a sorted array for entry equal to its index Search a cyclically sorted array Compute the integer square root Compute the real square root Search in a 2D sorted array Find the and max simultaneously Find the fcth largest element Find the missing IP address Find the duplicate and missing elements 191 192 194 195 196 198 199 202 203 13 Hash Tables 13.1 Test for palindromic permutations 13.2 13.3 13.4 13.5 13.6 13.7 13.8 13.9 13.10 13.11 13.12 13.13 13.14 212 213 214 216 217 217 218 222 224 225 227 228 230 231 Is an anonymous letter constructible? Implement an ISBN cache Compute the LCA, optimizing for close ancestors Compute the k most frequent queries Find the nearest repeated entries in an array Find the smallest subarray covering all values Find smallest subarray sequentially covering all values Find the longest subarray with distinct entries Find the length of a longest contained interval Compute the average of the top three scores Compute all string decompositions Test the Collatz conjecture Implement a hash function for chess 14 Sorting 14.1 Compute the intersection of two sorted arrays 14.2 Merge two sorted arrays 14.3 Remove first-name duplicates 14.4 Render a calendar 14.5 Merging intervals 14.6 Compute the union of intervals 14.7 Partitioning and sorting an array with many repeated entries 14.8 Team photo day 14.9 Implement a fast sorting algorithm for lists 14.10 Compute a salary threshold — 15 Binary Search Trees 15.1 15.2 15.3 15.4 15.5 15.6 207 234 236 238 239 240 242 244 246 248 250 252 254 Test if a binary tree satisfies the BST property Find the first key greater than a given value in a BST Find the k largest elements in a BST Compute the LCA in a BST Reconstruct a BST from traversal data Find the closest entries in three sorted arrays IV 256 259 260 261 262 265 15.7 15.8 15.9 15.10 15.11 15.12 15.13 Enumerate numbers of the form a + bÿJl The most visited pages problem Build a minimum height BST from a sorted array Insertion and deletion in a BST Test if three BST nodes are totally ordered The range lookup problem Add credits 267 269 271 272 275 276 279 16 Recursion 16.1 The Towers of Hanoi problem 16.2 16.3 16.4 16.5 16.6 16.7 16.8 16.9 16.10 16.11 282 283 285 287 289 291 292 293 295 296 298 300 Generate all nonattacking placements of n-Queens Generate permutations Generate the power set Generate all subsets of size k Generate strings of matched parens Generate palindromic decompositions Generate binary trees Implement a Sudoku solver Compute a Gray code Compute the diameter of a tree 17 Dynamic Programming 17.1 Count the number of score combinations 17.2 17.3 17.4 17.5 17.6 17.7 17.8 17.9 17.10 17.11 17.12 303 306 309 312 314 315 317 320 323 324 326 327 330 Compute the Levenshtein distance Count the number of ways to traverse a 2D array Compute the binomial coefficients Search for a sequence in a 2D array The knapsack problem The bedbathandbeyond.com problem Find the minimum weight path in a triangle Pick up coins for maximum gain Count the number of moves to climb stairs The pretty printing problem Find the longest nondecreasing subsequence 18 Greedy Algorithms and Invariants 18.1 Compute an optimum assignment of tasks 18.2 Schedule to minimize waiting time 18.3 The interval covering problem 18.4 The 3-sum problem 18.5 Find the majority element 18.6 The gasup problem 18.7 Compute the maximum water trapped by a pair of vertical lines 18.8 Compute the largest rectangle under the skyline v 333 334 335 336 340 341 343 345 347 the length of the longest subarray satisfying the sum constraint identified so far to b - a + and conditionally update it Consequently, we can increment b Suppose we initialize a and b to and iteratively perform the increments to a and b described above until b n Then we will discover the length of a maximum length subarray that satisfies the sum constraint We justify this claim after presenting an implementation of these ideas below = public static int findLongestSubarrayLessEqualK(List A, int k) { // Build the prefix sum according to A List prefixSum = new ArrayList (); int sum = ®; for (int a : A) { sum += a ; prefixSum add(sum) ; } returns if the sum of A is smaller than or equal to k if (prefixSum get(prefixSum size() 1) minPrefixSum = new ArrayList o(prefixSum); for (int i = minPrefixSum.size() ; i >= ®; i) { minPrefixSum set(i , Math min(minPrefixSum get(i), minPrefixSum get(i - i))); } int a = ® , b = ® , maxLength = ®; while (a < A.sizeO ® ? minPrefixSum get(b) : minPrefixSum get(b); if (minCurrSum maxLength) { maxLength = currLength; - prefixSum get(a - 1) } ++b ; } else { // minCurrSum > k ++a ; } } return maxLength; Now we argue the correctness of the program Let A [a* : b*] be a maximum length subarray that satisfies the sum constraint Note that we increment b until Ma>b > k In particular, when we increment a to a + 1, A[a : b 1] does satisfy the sum constraint, but A[a : b] does not This implies A[a : b 1] is the longest subarray starting at a that satisfies the sum constraint The iteration ends when b = n At this point, we claim > a* If not, then A [a : n-1] satisfies the sum constraint, since we incremented b to n, and (n - 1) - a + > b* -a* + 1, contradicting the optimality of A[a* : b*] Therefore, a must be assigned to a* at some iteration At this point, b < b* since A [a* - : b - 1] satisfies the sum constraint For, — 508 — if b > b' , then (b - 1) - (a* - 1) + = b - a’ + > b' - a' + 1, violating the maximality of A[a’ : b’] Since b < b' and a = a", the algorithm will increment b till it becomes b' (since A[a' : b'] satisfies the sum constraint), and thus will identify b’ a’ + as the optimum solution — Variant: Design an algorithm for finding the longest subarray of a given array such that the average of the subarray elements is < k 25.39 ROAD NETWORK O' The California Department of Transportation is considering adding a new section of highway to the California Highway System Each highway section connects two cities City officials have submitted proposals for the new highway each proposal includes the pair of cities being connected and the length of the section — Write a program which takes the existing highway network (specified as a set of highway sections between pairs of cities) and proposals for new highway sections, and returns the proposed highway section which leads to the most improvement in the total driving distance The total driving distance is defined to be the sum of the shortest path distances between all pairs of cities All sections, existing and proposed, allow for bi-directional traffic, and the original network is connected Hint: Suppose we add a new section from bs to bf If the shortest path from utov passes through this section, what must be true of the part of the path from u to bs7 Solution: Note that we cannot add more than one proposal to the existing network and run a shortest path algorithm we may end up with a shortest path which uses multiple proposals The brute-force approach would be to first compute the shortest path distances for all pairs in the original network Then consider the new sections, one-at-a-time, and then compute the new shortest path distances for all pairs, recording the total improvement The all-pairs shortest path problem can be solved in time 0(n3) using the Floyd-Warshall algorithm, leading to an overall 0(kn3) time complexity We can improve upon this by running the all pairs shortest paths algorithm just once Let S(u,v) be the 2D array of shortest path distances for each pair of cities Each proposal p is a pair of cities (x, y) For the pair of cities (a,b), the best we can by using proposal p is min(S(a, b), S(a, x) + d(x, y) + S(y, b), S(a, y) + d(y, x) + S(x,b)) where d(x, y) is the distance of the proposed highway p between x and y This computation is 0(1) time, so we can evaluate all the proposals in time proportional to the number of proposals times the number of pairs after we have computed the shortest path between each pair of cities This results in an 0(n3 + kn2) time complexity, which improves substantially on the brute-force approach — public static class HighwaySection { public int x, y; public double distance ; public HighwaySection(int x, int y, double distance) { this.x = x; 509 this y = y; this distance = distance; } } public static HighwaySect ion findBestProposals(List H, List cHighwaySect ion> P, int n) { // G stores the shortest path distances between all pairs of vertices List G = new ArrayList (n); for (int i = ®; i < n; ++i) { G.add(new ArrayList(Collect ions nCopies(n , Double MAX_VALUE))); } for (int i = ®; i < n; ++i) { G get(i).set(i, ®.®); } // Builds an undirected graph G based on existing highway sections H for (HighwaySect ion h : H) { G get(h x) set(h.y , h.distance); G get(h.y) set(h x , h.distance); } // Performs Floyd Warshall to build the shortest path between vertices floydWarshall(G); // Examines each proposal for shorter distance for all pairs double bestDistanceSaving = Double MIN_VALUE ; HighwaySect ion bestProposal = new HighwaySection(-l , -1, ®.®); // Default for (HighwaySect ion p : P) { double proposalSaving = 8.8; for (int a = ® ; a < n; ++a) { for (int b = ® ; b < n; ++b) { double saving = G get(a).get(b) (G get(a) get(p x) + p.distance + G get(p y) get(b)); - proposalSaving += saving > ®.® ? saving : ®.®; } } if (proposalSaving > bestDistanceSaving) { bestDistanceSaving bestProposal = p; = proposalSaving; } } return bestProposal ; private static void floydWarshall(List G) { for (int k = 8; k < G.size(); ++k) { for (int i = 8; i < G.size(); ++i) { for (int j = 8; j < G.size(); ++j) { if (G get(i) get(k) != Double MAX_VALUE && G get(k) get(j) != Double MAX_VALUE && G get(i).get(j) > G.get(i).get(k) + G get(k) get(j)){ G get(i).set(j , G get(i).get(k) + G get(k).get(j) ); } } } 510 > } 25.40 TEST IF ARBITRAGE IS POSSIBLE Q5 You are exploring the remote valleys of Papua New Guinea, one of the last uncharted places in the world You come across a tribe that does not have money instead it relies on the barter system A total of n commodities are traded and the exchange rates are specified by a 2D array For example, three sheep can be exchanged for seven goats and four goats can be exchanged for 200 pounds of wheat Transaction costs are zero, exchange rates not fluctuate, fractional quantities of items can be sold, and the exchange rate between each pair of commodities is finite Table 4.4 on Page 35 shows exchange rates for currency trades, which is similar in spirit to the current problem — — Design an efficient algorithm to determine whether there exists an arbitrage a way to start with a single unit of some commodity C and convert it back to more than one unit of C through a sequence of exchanges Hint: The effect of a sequence of conversions is multiplicative Can you recast the problem so that it can be calculated additively? Solution: We define a weighted directed graph G = (V, E = V X V), where V corre¬ sponds to the set of commodities The weight w(e) of edge e = («, v) is the amount of commodity v we can buy with one unit of commodity u Observe that an arbitrage exists if and only if there exists a cycle in G whose edge weights multiply out to more than Create a new graph G' = (V, E) with weight function w'(e) = -lg w(e) Since lg(a X b) = lg a + lg b, there exists a cycle in G whose edge weights multiply out to more than if and only if there exists a cycle in G' whose edge weights sum up to less than lg = (This property is true for logarithms to any base, so if it is more efficient for example to use base-e, we can so.) The Bellman-Ford algorithm detects negative-weight cycles Usually, finding a negative-weight cycle is done by adding a dummy vertex s with 0-weight edges to each vertex in the given graph and running the Bellman-Ford single-source shortest path algorithm from s However, for the arbitrage problem, the graph is complete Hence, we can run Bellman-Ford algorithm from any single vertex, and get the right result public static boolean isArbitrageExist(List G) { // Transforms each edge in G for (List edgeList : G) { for (int i = ®; i < edgeList size(); i++) { edgeList.set(i , -Math logl®(edgeList.get(i))); } } // Uses Bellman-Ford to find negative weight cycle return bellmanFord(G, ®); 511 } private static boolean bellmanFord(List G, int source) { List disToSource = new ArrayList ( Collect ions nCopies(G size(), Double MAX_VALUE)); disToSource set(source , ®.®); for (int times = 1; times < G.size(); ++times) { boolean haveUpdate = false; for (int i = ®; i < G.size(); ++i) { for (int j = ®; j < G get(i).size(); ++j){ if (disToSource get(i) != Double MAX_VALUE & disToSource get(i) + G get(i).get(j) ){ haveUpdate = true; disToSource set(j , disToSource get(i) + G get(i).get(j) ); } } } // No update in this iteration means no negative cycle if (!haveUpdate){ return false ; } } // Detects cycle if there is any further update for (int i = ®; i < G.size(); ++i) { for (int j = ®; j < G get(i).size(); ++j){ if (disToSource get(i) != Double MAX_VALUE