When it Goes Wrong - How to Ace System Design Interview
How to Land Your Dream Job Series
The Approach
- It’s important to follow some systematic approach and drive/lead this conversation.
- SNAKE(Scenario, Needs, Application, Kilobyte(Data), Evolve)
- Requirements, constraints/estimates, high-level design, detailed design
- Talk about different approaches, pros and cons
- It’s more important to follow the approach when you don’t have (much) clue or the interviewer wants to focus on specific domain or jump to detail too early.
System design interview is very important, as it’s usually conducted by managers or senior engineers who have more power to decide to hire or not, or the level.
Different from coding interview, how the system design interview goes varies a lot. Some companies(like Facebook) follow some good process, but others are not.
Depended on the interviewer, or what the candidate says, the system design interview may go wrong pretty soon.
1. Algorithm/Coding Question as a part of System Design
- During the middle of the discussion, the interviewer may (unexpectedly) ask algorithm question.
Example: Autocomplete Service
- As a system design question, the candidate may talk about: requirement
- for a distributed prefix tree, how to shard data: consistent hash etc.
- The interviewer may also ask and focus on how to Get top x word (ranked by frequency) from a single server trie: the algorithm question.
- The candidate should be aware of the change, and approach it as an algorithm question and quickly give an optimal algorithm/pseudo code quickly.
2. The interviewer looking for specific direction/answer
Focus on detail too soon and too much
- There are a lot of things the candidate can talk, but sometimes, the interviewer is looking for some specific direction/answer.
- This is not good, as the system design is supposed to let the candidate lead the process, but you have to try your best when you are in this situation.
Example: One backend key value db server is slow, how to solve it?
The interviewer may jump to how to use cache, before you go that direction, make sure you also(still) talk about the scenario, outline other approaches first.
You may not have much time to go through those: you have to take and talk a lot about X when the interviewer is saying: let's go X approach, but still outline them in the whiteboard, at least you can resume those later.
- Make sure ask these questions first
- does the backend provide client library?
- How many client apps? How easy to update them to new code/library?
- What’s the goal? How much effort? a short term fix to just make it work? or a long term invest
- What’s the team structure? Who is responsible to fix it (what team has the resource)? DB team or client team?
- Potential solutions/discussion:
- local cache in client side or remote central cache
- request coalescing in client library
- distribute the db: shard the database and add a proxy layer which can distribute the request to the right shard
- Stability: rate limit, etc
What We Can Do?
Do Not Repeat Yourself
- When asked the same questions after you already give (some) answer about it. Don’t just repeat your previous answer or try to expand it.
- Usually, this means your answer is not what the interviewer is looking for at all.
- Talk something meaningful and useful, that shows your skills.
Know/Guess what’s the interview is looking for
How vs Why and What
In system design, how to do it in detail is usually not important, instead focus on why and what info we are trying to get.
Between approach x and y, how you can determine which approach to take?With the system is already running, how can you decide you should add local cache or centralized cache?- THIS IS NOT IMPORTANT:
how to add the log, what to log and how to analyze log - WHAT IS IMPORTANT: how to determine the cache performance: hit ratio.
- THIS IS NOT IMPORTANT:
System Design Books
- Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems
- The Art of Scalability: Scalable Web Architecture, Processes, and Organizations for the Modern Enterprise, Second Edition
- Web Scalability for Startup Engineers
System Design Prep Resource
Graph Algorithm - How to Succeed in Algorithms Interview
Series: How to Succeed in Algorithms Interview
Graph represent
- AdjacencyMatrix
- AdjacencyList: List
[] - EdgeList: Edge[] edges
- Map<Integer, Set
> map - Map
indegrees - Topological Sort O(V+E)
- create graph when 2 elements meets some criteria
Approaches
Binary tree to graph
- Annotate Parent
- Create parent map: Map<TreeNode, TreeNode> map
- create Map<TreeNode, List
> map - LeetCode 863 - All Nodes Distance K in Binary Tree
- LeetCode 815 - Bus Routes
- create graph: from stop to route id then bfs
- LeetCode 996 - Number of Squareful Arrays
int N;
Map<Integer, List<Integer>> graph;
Integer[][] memo;
public int numSquarefulPerms(int[] A) {
N = A.length;
graph = new HashMap();
memo = new Integer[N][1 << N];
for (int i = 0; i < N; ++i)
graph.put(i, new ArrayList());
for (int i = 0; i < N; ++i)
for (int j = i + 1; j < N; ++j) {
int r = (int) (Math.sqrt(A[i] + A[j]) + 0.5);
if (r * r == A[i] + A[j]) {
graph.get(i).add(j);
graph.get(j).add(i);
}
}
int[] factorial = new int[20];
factorial[0] = 1;
for (int i = 1; i < 20; ++i)
factorial[i] = i * factorial[i - 1];
int ans = 0;
for (int i = 0; i < N; ++i)
ans += dfs(i, 1 << i);
Map<Integer, Integer> count = new HashMap();
for (int x : A)
count.put(x, count.getOrDefault(x, 0) + 1);
for (int v : count.values())
ans /= factorial[v];
return ans;
}
public int dfs(int node, int visited) {
if (visited == (1 << N) - 1)
return 1;
if (memo[node][visited] != null)
return memo[node][visited];
int ans = 0;
for (int nei : graph.get(node))
if (((visited >> nei) & 1) == 0)
ans += dfs(nei, visited | (1 << nei));
memo[node][visited] = ans;
return ans;
}Detect Cycle in Undirected Graph
- dfs: O(V+E)
- During DFS, for any current vertex ‘x’ if there an adjacent vertex ‘y’ is present which is already visited and ‘y’ is not a direct parent of ‘x’ then there is a cycle in graph.
- boolean isCycleUtil(int currVertex, boolean [] visited, int parent)
- union find: O(ELogV)
Detect Cycle in a Directed Graph
- dfs
- Recursion stack[] is used from keep track of visiting vertices during DFS from particular vertex and gets reset once cycle is not found from that vertex and will try DFS from other vertices.
- isCycleUtil(int vertex, boolean[] visited, boolean[] recursiveArr)
- dfs + using colors
- Topological sort
Check if a directed graph is strongly connected
- Do a DFS traversal of graph starting from any arbitrary vertex v. If DFS traversal doesn’t visit all vertices, then return false.
- Reverse all arcs (or find transpose or reverse of graph) and Do a DFS traversal of reversed graph starting from same vertex , If DFS traversal doesn’t visit all vertices, then return false.
- The idea is, if every node can be reached from a vertex v, and every node can reach v, then the graph is strongly connected. In step 1, we check if all vertices are reachable from v. In step 2, we check if all vertices can reach v (In reversed graph, if all vertices are reachable from v, then all vertices can reach v in original graph)
Eulerian path
Eulerian Cycle
- For an undirected graph has Eulerian cycle
- All vertices with non-zero degree are connected.
- All vertices have even degree.
Eulerian Path
- For an undirected graph has Eulerian Path
- Same as condition (a) for Eulerian Cycle
- If two vertices have odd degree and all other vertices have even degree. Note that only one vertex with odd degree is not possible in an undirected graph (sum of all degrees is always even in an undirected graph)
Eulerian Cycle for a directed graph
- A directed graph has an eulerian cycle if following conditions are true
- All vertices with nonzero degree belong to a single strongly connected component.
- In degree and out degree of every vertex is same.
Hamiltonian Cycle
Connected Components
- A Strongly connected component is a sub-graph where there is a path from every node to every other node.
- A weakly connected component is one in which all components are connected by some path, ignoring direction
- LeetCode 323 - Number of Connected Components in an Undirected Graph
- bfs, dfs, union find
- LintCode 432 - Find the Weak Connected Component in the Directed Graph
Matrix to Graph
Tree to Graph
- LeetCode 863 - All Nodes Distance K in Binary Tree
- Create graph: Map<TreeNode, List
> map = new HashMap<>(); - Clone and add Parent Node
- Create graph: Map<TreeNode, List
Color during DFS
- a state = {Initial=0, InProgress=1, Completed=2 }
- LeetCode 785 - Is Graph Bipartite?
- -1: Haven’t been colored, 0: Blue, 1: Red.
- LeetCode 886 - Possible Bipartition
Create Reverse Graph
Degree(in-degree, out-degree)
public int findJudge(int N, int[][] trust) {
int[] count = new int[N+1];
for (int[] t: trust) {
count[t[0]]--;
count[t[1]]++;
}
for (int i = 1; i <= N; ++i) {
if (count[i] == N - 1) return i;
}
return -1;
}Floyd Warshall Algorithm - All Pairs Shortest Path: O(V^3)
- one by one pick all vertices and updates all shortest paths which include the picked vertex as an intermediate vertex in the shortest path.
- when we pick vertex number k as an intermediate vertex, we already have considered vertices {0, 1, 2, .. k-1} as intermediate vertices.
void floydWarshall(int graph[][])
{
int dist[][] = new int[V][V];
int i, j, k;
for (i = 0; i < V; i++)
for (j = 0; j < V; j++)
dist[i][j] = graph[i][j];
// Add all vertices one by one to the set of intermediate vertices.
for (k = 0; k < V; k++)
{
// Pick all vertices as source one by one
for (i = 0; i < V; i++)
{
// Pick all vertices as destination for the
// above picked source
for (j = 0; j < V; j++)
{
// If vertex k is on the shortest path from i to j, then update the value of dist[i][j]
if (dist[i][k] + dist[k][j] < dist[i][j])
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}- LeetCode 399 - Evaluate Division
- create edge for a to b and b to a
- dfs+cache: O(e+q*e)
- bfs
- Floyd–Warshall
- best: union-find - store ration in parent node: O(e+q)
public double[] calcEquation(String[][] equations, double[] values, String[][] queries) { HashMap<String, HashMap<String, Double>> graph = new HashMap<>(); Function<String, HashMap<String, Double>> function = s -> new HashMap<>(); for (int i = 0; i < equations.length; i++) { graph.computeIfAbsent(equations[i][0], function).put(equations[i][0], 1.0); graph.computeIfAbsent(equations[i][1], function).put(equations[i][1], 1.0); graph.get(equations[i][0]).put(equations[i][1], values[i]); graph.get(equations[i][1]).put(equations[i][0], 1 / values[i]); } for (String mid : graph.keySet()) { for (String src : graph.get(mid).keySet()) { for (String dst : graph.get(mid).keySet()) { double val = graph.get(src).get(mid) * graph.get(mid).get(dst); graph.get(src).put(dst, val); } } } double[] result = new double[queries.length]; for (int i = 0; i < result.length; i++) { if (!graph.containsKey(queries[i][0])) { result[i] = -1; } else { result[i] = graph.get(queries[i][0]).getOrDefault(queries[i][1], -1.0); } } return result; }
Examples
Reverse Thinking - How to Succeed in Algorithms Interview
How to Succeed in Algorithms Interview Series
Scan from right to left
- From end to start instead from start to end
- when element’s value/state is related with afterwards elements
- use stack
- or maybe traverse from end
- LeetCode 844 - Backspace String Compare
public boolean backspaceCompare(String S, String T) {
for (int i = S.length() - 1, j = T.length() - 1;; i--, j--) {
for (int b = 0; i >= 0 && (b > 0 || S.charAt(i) == '#'); --i) b += S.charAt(i) == '#' ? 1 : -1;
for (int b = 0; j >= 0 && (b > 0 || T.charAt(j) == '#'); --j) b += T.charAt(j) == '#' ? 1 : -1;
if (i < 0 || j < 0 || S.charAt(i) != T.charAt(j)) return i == -1 && j == -1;
}
}- LeetCode 174 - Dungeon Game: minimum initial health so that he is able to rescue the princess
- reverse thing: from princess to knight
dp[i][j] = max(1, min(dp[i][j+1] - mat[i][j], dp[i+1][j] - mat[i][j]))
- LeetCode 55 - Jump Game
- track maxReach: we don’t care what positions a[i] can reach, only the maxReach
- LeetCode 769 - Max Chunks To Make Sorted
- LeetCode 45 - Jump Game II: find the minimum number of jumps
- LeetCode 482 - License Key Formatting
- HackerRank: Stock Maximize
- Leaders in an array
- LeetCode 439 - Ternary Expression Parser
public String parseTernary(String expression) {
if (expression == null || expression.length() == 0) return "";
Deque<Character> stack = new LinkedList<>();
for (int i = expression.length() - 1; i >= 0; i--) {
char c = expression.charAt(i);
if (!stack.isEmpty() && stack.peek() == '?') {
stack.pop(); //pop '?'
char first = stack.pop();
stack.pop(); //pop ':'
char second = stack.pop();
if (c == 'T') stack.push(first);
else stack.push(second);
} else {
stack.push(c);
}
}
return String.valueOf(stack.peek());
}public String decodeAtIndex(String S, int K) {
long size = 0;
int N = S.length();
for (int i = 0; i < N; ++i) {
char c = S.charAt(i);
if (Character.isDigit(c))
size *= c - '0';
else
size++;
}
for (int i = N - 1; i >= 0; --i) {
char c = S.charAt(i);
K %= size;
if (K == 0 && Character.isLetter(c))
return Character.toString(c);
if (Character.isDigit(c))
size /= c - '0';
else
size--;
}
throw null;
}public String decodeAtIndex(String S, int K) {
long size = 0;
int N = S.length();
// Find size = length of decoded string
for (int i = 0; i < N; ++i) {
char c = S.charAt(i);
if (Character.isDigit(c))
size *= c - '0';
else
size++;
}
for (int i = N - 1; i >= 0; --i) {
char c = S.charAt(i);
K %= size;
if (K == 0 && Character.isLetter(c))
return Character.toString(c);
if (Character.isDigit(c))
size /= c - '0';
else
size--;
}
throw null;
}- LeetCode 853 - Car Fleet
- from closet to furthest
public int carFleet(int target, int[] position, int[] speed) { TreeMap<Integer, Integer> map = new TreeMap<>(); int n = position.length; for(int i=0; i<n; ++i){ map.put(target - position[i], speed[i]); } int count = 0; double r = -1.0; for(Map.Entry<Integer, Integer> entry: map.entrySet()){ int d = entry.getKey(); // distance int s = entry.getValue(); // speed double t = 1.0*d/s; // time to target if(t>r){ // this car is unable to catch up previous one, form a new group and update the value ++count; r = t; } } return count; }
From (potential) target to source
- LeetCode 780 - Reaching Points
- if we start from sx and sy, there is two possibility, but if we start from target, there is only one possibility
- use modulo to speed minus
bool reachingPoints(int sx, int sy, int tx, int ty) {
while(tx >= sx && ty >= sy){
if(tx > ty){
if(sy == ty) return (tx - sx) % ty == 0;
tx %= ty;
}else{
if(sx == tx) return (ty - sy) % tx == 0;
ty %= tx;
}
}
return false;
}
public boolean reachingPoints(int sx, int sy, int tx, int ty) {
if (sx > tx || sy > ty) return false;
if (sx == tx && (ty - sy) % sx == 0) return true;
if (sy == ty && (tx - sx) % sy == 0) return true;
return reachingPoints(sx, sy, tx % ty, ty % tx);
}- LeetCode 174 - Dungeon Game
- LeetCode 55 - Jump Game
- track maxReach
- from target to source
- LeetCode 477 - Largest Palindrome Product
- from potential target palindrome
- HARD LeetCode 991 - Broken Calculator: Double or ++
- guess, assume and prove/anti-example
- from target to source
- LeetCode 803 - Bricks Falling When Hit
- reverse thinking: from end state + union find
res[i] = (newSize - count > 0) ? newSize - count - 1 : 0;
- reverse thinking: from end state + union find
Reverse
- LeetCode 186 Reverse Words in a String II: in-place
- LeetCode 189 - Rotate an array right by k element
- Sort the sequence using pancake sorting as few reversals as possible
- LeetCode 969 - Pancake Sorting
- swap max to top
- swap max to bottom
- reduce size then repeat
Reverse - call same function again
- Find maximum difference between nearest left and right smaller elements
- reverse and call same function again
Guess
- [LeetCode 843 - Guess the Word]
- [minimum our worst outcome]
- worse case: only 0 match,
- we guess the word with minimum words of 0 matches
- [minimum our worst outcome]
// random
public void findSecretWord(String[] wordlist, Master master) {
for (int i = 0, x = 0; i < 10 && x < 6; ++i) {
String guess = wordlist[new Random().nextInt(wordlist.length)];
x = master.guess(guess);
List<String> wordlist2 = new ArrayList<>();
for (String w : wordlist)
if (match(guess, w) == x)
wordlist2.add(w);
wordlist = wordlist2.toArray(new String[wordlist2.size()]);
}
}
public void findSecretWord(String[] wordlist, Master master) {
for (int i = 0, x = 0; i < 10 && x < 6; ++i) {
HashMap<String, Integer> count = new HashMap<>();
for (String w1 : wordlist)
for (String w2 : wordlist)
if (match(w1, w2) == 0)
count.put(w1, count.getOrDefault(w1 , 0) + 1);
Pair<String, Integer> minimax = new Pair<>("", 1000);
for (String w : wordlist)
if (count.getOrDefault(w, 0) < minimax.getValue())
minimax = new Pair<>(w, count.getOrDefault(w, 0));
x = master.guess(minimax.getKey());
List<String> wordlist2 = new ArrayList<String>();
for (String w : wordlist)
if (match(minimax.getKey(), w) == x)
wordlist2.add(w);
wordlist = wordlist2.toArray(new String[0]);
}
}Sliding Window - How to Succeed in Algorithms Interview
How to Succeed in Algorithms Interview Series
Applications of Sliding Window
- window of size k
- Shortest/longest Subarray with xxx
- continuous subarrays or substrings
How to Implement Sliding Window
- expand (end pointer) the window until it meets the criteria
- reset value when it violates
- shrink (start pointer) to make it smallest
- maintain states when expand and shrink: put in a map/set
- use together with queue or monotone queue
Implementation Detail
Multiple List
- LeetCode 632 - Smallest Range (shortest range in k-sorted lists)
- LeetCode 3 - Longest Substring Without Repeating Characters
public int lengthOfLongestSubstring(String s) {
if (s.length()==0) return 0;
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
int max=0;
for (int i=0, j=0; i<s.length(); ++i){
if (map.containsKey(s.charAt(i))){
j = Math.max(j,map.get(s.charAt(i))+1);
}
map.put(s.charAt(i),i);
max = Math.max(max,i-j+1);
}
return max;
}- Find zeroes to be flipped so that number of consecutive 1’s is maximized
public int lengthOfLongestSubstringKDistinct(String str, int k) { if (str == null || str.isEmpty() || k == 0) { return 0; } TreeMap<Integer, Character> lastOccurrence = new TreeMap<>(); Map<Character, Integer> inWindow = new HashMap<>(); int j = 0; int max = 1; for (int i = 0; i < str.length(); i++) { char in = str.charAt(i); while (inWindow.size() == k && !inWindow.containsKey(in)) { int first = lastOccurrence.firstKey(); char out = lastOccurrence.get(first); inWindow.remove(out); lastOccurrence.remove(first); j = first + 1; } //update or add in's position in both maps if (inWindow.containsKey(in)) { lastOccurrence.remove(inWindow.get(in)); } inWindow.put(in, i); lastOccurrence.put(i, in); max = Math.max(max, i - j + 1); } return max; } - LeetCode 340 - Longest Substring with At Most K Distinct Characters
- Sum of minimum and maximum elements of all subarrays of size k
- remove numbers out of range k
- remove numbers in k range as they are useless
- remove numbers out of range k
- LeetCode 67 - Minimum Window Substring
- expand (end pointer) the window until it meets the criteria
- shrink (start pointer) to make it smallest
- expand (end pointer) the window until it meets the criteria
- LeetCode 424 - Longest Repeating Character Replacement
- LeetCode 1004 - Max Consecutive Ones III
- LeetCode 904 - Fruit Into Baskets
- LeetCode 438 - Find All Anagrams in a String
public List<Integer> findAnagrams(String s, String p) {
Map<Character, Integer> map = counter(p);
int match = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (map.containsKey(c)) {
map.put(c, map.get(c) - 1);
if (map.get(c) == 0) {
match++;
}
}
if (i >= p.length()) {
c = s.charAt(i - p.length());
if (map.containsKey(c)) {
map.put(c, map.get(c) + 1);
if (map.get(c) == 1) {
match--;
}
}
}
if (match == map.size()) {
result.add(i - p.length() + 1);
}
}
return result;
}Window class
- LeetCode 992 - Subarrays with K Different Integers
- Given an array A of positive integers, call a (contiguous, not necessarily distinct) subarray of A good if the number of different integers in that subarray is exactly K.
- exactly -> at most
- sliding window, two pointers
- window class, maintain 2 sliding windows with same end element
public int subarraysWithKDistinct(int[] A, int K) { Window window1 = new Window(); Window window2 = new Window(); int ans = 0, left1 = 0, left2 = 0; for (int right = 0; right < A.length; ++right) { int x = A[right]; window1.add(x); window2.add(x); while (window1.different() > K) window1.remove(A[left1++]); while (window2.different() >= K) window2.remove(A[left2++]); ans += left2 - left1; } return ans; }Jumping Window + Fixed Size
- Given an array A of positive integers, call a (contiguous, not necessarily distinct) subarray of A good if the number of different integers in that subarray is exactly K.
- LeetCode 683 - K Empty Slots
- sliding window: find a match when i reaches end of current window
- [O(kn) brute force]
- TreeSet: lower/higher
- Bucket
public int kEmptySlots(int[] flowers, int k) { int[] days = new int[flowers.length]; for (int i = 0; i < flowers.length; i++) days[flowers[i] - 1] = i + 1; int left = 0, right = k + 1, result = Integer.MAX_VALUE; for (int i = 0; right < days.length; i++) { if (days[i] < days[left] || days[i] <= days[right]) { if (i == right) result = Math.min(result, Math.max(days[left], days[right])); left = i; right = k + 1 + i; } } return (result == Integer.MAX_VALUE) ? -1 : result; } - sliding window: find a match when i reaches end of current window
Using Priority Queue - How to Succeed in Algorithms Interview
How to Succeed in Algorithms Interview Series
Applications of Priority Queue
- TopK
Implementation
- Use poll, peek, offer
- Don’t use remove (if need update it: just keep the old one and add a new one)
- Alternation: use TreeSet/Map
Order - How to add elements
- LeetCode - 719 Find K-th Smallest Pair Distance
- add neighbor pairs first
for (int i = 0; i + 1 < nums.length; ++i) {
heap.offer(new Node(i, i+1));
}
Node node = null;
for (; k > 0; --k) {
node = heap.poll();
if (node.nei + 1 < nums.length) {
heap.offer(new Node(node.root, node.nei + 1));
}
}Merge k sorted list
- Element: [outerIndex, innerIndex]
- Iterator
Examples
- LeetCode 23 - Merge k Sorted Lists
- LeetCode 632 - Smallest Range (shortest range in k-sorted lists)
- LeetCode 759 - Employee Free Time
public List<Interval> employeeFreeTime(List<List<Interval>> schedule) {
List<Interval> res = new ArrayList<Interval>();
PriorityQueue<Node> minHeap = new PriorityQueue<Node>(
(a, b) -> schedule.get(a.employee).get(a.index).start - schedule.get(b.employee).get(b.index).start);
int start = Integer.MAX_VALUE;
for (int i = 0; i < schedule.size(); i++) {
minHeap.add(new Node(i, 0));
start = Math.min(start, schedule.get(i).get(0).start);
}
while (!minHeap.isEmpty()) {
Node cur = minHeap.poll();
if (start < schedule.get(cur.employee).get(cur.index).start) {
res.add(new Interval(start, schedule.get(cur.employee).get(cur.index).start));
}
start = Math.max(start, schedule.get(cur.employee).get(cur.index).end);
cur.index++;
if (cur.index < schedule.get(cur.employee).size()) {
minHeap.add(cur);
}
}
return res;
}
class Node {
int employee;
int index;
public Node(int employee, int index) {
this.employee = employee;
this.index = index;
}
}Examples
- LeetCode 264 - Ugly Number II
- O(nk) or O(nlogk)
- LeetCode 313 - Super Ugly Number
- O(NlogK) or O(nk)
public int nthSuperUglyNumber(int n, int[] primes) {
int[] ugly = new int[n];
int[] idx = new int[primes.length];
int[] val = new int[primes.length];
Arrays.fill(val, 1);
int next = 1;
for (int i = 0; i < n; i++) {
ugly[i] = next;
next = Integer.MAX_VALUE;
for (int j = 0; j < primes.length; j++) {
//skip duplicate and avoid extra multiplication
if (val[j] == ugly[i]) val[j] = ugly[idx[j]++] * primes[j];
//find next ugly number
next = Math.min(next, val[j]);
}
}
return ugly[n - 1];
}
public int nthSuperUglyNumberHeap(int n, int[] primes) {
int[] ugly = new int[n];
PriorityQueue<Num> pq = new PriorityQueue<>();
for (int i = 0; i < primes.length; i++) pq.add(new Num(primes[i], 1, primes[i]));
ugly[0] = 1;
for (int i = 1; i < n; i++) {
ugly[i] = pq.peek().val;
while (pq.peek().val == ugly[i]) {
Num nxt = pq.poll();
pq.add(new Num(nxt.p * ugly[nxt.idx], nxt.idx + 1, nxt.p));
}
}
return ugly[n - 1];
}- LeetCode 871 - Minimum Number of Refueling Stops
- Greedy + PQ, curFarthest: O(NlogN)
- Use PQ to store potential refuel stations
- DP: variables/states: station, refuel stops
- dp[i][j] the farthest location we can get to using exactly j refueling stops among the first i refueling stops for j<i; dp[i][j] = max(dp[i][j], dp[i-1][j-1] + stations[i][1], dp[i-1][j])
- reduce space: dp[t] means the furthest distance that we can get with t times of refueling
- Greedy + PQ, curFarthest: O(NlogN)
public int minRefuelStops(int target, int startFuel, int[][] stations) {
int curFarthest = startFuel, refuel = 0;
PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> b - a);
for (int[] station : stations) {
// check if we can reach this station
// if we cannot reach this station, refuel the gas from the previous station with most gas
// redo the operation until we get enough gas to reach this station
while (curFarthest < station[0]) {
if (pq.isEmpty()) return -1; // if we reful in each station but still cannot reach this station, return -1
curFarthest += pq.poll();
refuel++;
}
pq.offer(station[1]);
}
// now we have reached the last station, check if we can reach the target
while (curFarthest < target) {
if (pq.isEmpty()) return -1;
curFarthest += pq.poll();
refuel++;
}
return refuel;
}
public int minRefuelStops(int target, int cur, int[][] s) {
Queue<Integer> pq = new PriorityQueue<>();
int i = 0, res;
for (res = 0; cur < target; res++) {
while (i < s.length && s[i][0] <= cur)
pq.offer(-s[i++][1]);
if (pq.isEmpty()) return -1;
cur += -pq.poll();
}
return res;
}Poll multiple elements - slots
- Poll multiple elements into a temp list, make some change then add them back
- starting point: max element
- LeetCode 767 - Reorganize String: no duplicate characters are adjacent to each other
- LeetCode 358 - Rearrange String k Distance Apart
public String rearrangeString(String str, int k) {
if(k<=1){ return str; }
int[] count = new int[26];
for(int i=0; i<str.length(); i++){
count[str.charAt(i)-'a']++;
}
PriorityQueue<int[]> pq = new PriorityQueue<>((a,b)->b[0]-a[0]);
for(int i=0; i<count.length; i++){ pq.add(new int[]{ count[i], i}); }
char[] result = new char[str.length()];
int idx = 0;
int start = 0;
while(!pq.isEmpty()){
int[] num = pq.remove();
for(int i=0; i<num[0]; i++){
result[idx] = (char)(num[1]+'a');
if(idx>0 && result[idx-1]==result[idx]){ return ""; }
idx+=k;
if(idx>=str.length()){ idx=++start; }
}
}
return new String(result);
}
public String rearrangeString(String str, int k) {
for(char c: map.keySet())
queue.offer(c);
StringBuilder sb = new StringBuilder();
int len = str.length();
while(!queue.isEmpty()){
int cnt = Math.min(k, len);
ArrayList<Character> temp = new ArrayList<Character>();
for(int i=0; i<cnt; i++){
if(queue.isEmpty())//\\
return "";
char c = queue.poll();
sb.append(String.valueOf(c));
map.put(c, map.get(c)-1);
if(map.get(c)>0){
temp.add(c);
}
len--;
}
for(char c: temp)
queue.offer(c);
}
return sb.toString();
}- LeetCode 621 - Task Scheduler
- math: (maxFreq -1)x(interval+1)+(cnt of maxFreq)
- PriorityQueue or Sort, output solution
public int leastInterval(char[] tasks, int n) {
Map<Character, Integer> counts = new HashMap<Character, Integer>();
for (char t : tasks) {
counts.put(t, counts.getOrDefault(t, 0) + 1);
}
PriorityQueue<Integer> pq = new PriorityQueue<Integer>((a, b) -> b - a);
pq.addAll(counts.values());
int alltime = 0;
int cycle = n + 1;
while (!pq.isEmpty()) {
int worktime = 0;
List<Integer> tmp = new ArrayList<Integer>();
for (int i = 0; i < cycle; i++) {
if (!pq.isEmpty()) {
tmp.add(pq.poll());
worktime++;
}
}
for (int cnt : tmp) {
if (--cnt > 0) {
pq.offer(cnt);
}
}
alltime += !pq.isEmpty() ? cycle : worktime;
}
return alltime;
}Min/MaxHeap(TreeSet)
- LeetCode 295 - Find Median from Data Stream
- POJ 4302 Holedox Eating
- POJ 2010 Moo University – Financial Aid
BFS + PriorityQueue
Dijkstra
- O(E+VlogV)
- frontier, settled set, expand
- PriorityQueue
- LeetCode 743 - Network Delay Time
- we send a signal from a certain node K. How long will it take for all nodes to receive the signal?
- use dist map as visited, no need to remove: O(ElogE)
- LeetCode 505 - The Maze II: ball roll
- LeetCode 499 - The Maze III: ball, roll, move in to the hole
- LeetCode 787 - Cheapest Flights Within K Stops
- LeetCode 778 - Swim in Rising Water
- find a path whose max is minimum
- PQ+BFS: O(N^2logN)
- Bisection + use bfs/dfs to validate
- Bisection + use union find to validate
Examples
public int kthSmallest(final int[][] matrix, int k) {
int c = 0;
PriorityQueue<int[]> queue = new PriorityQueue<>(
k, (o1, o2) -> matrix[o1[0]][o1[1]] - matrix[o2[0]][o2[1]]);
queue.offer(new int[] {0, 0});
while (true) {
int[] pair = queue.poll();
if (++c == k) {
return matrix[pair[0]][pair[1]];
}
if (pair[0] == 0 && pair[1] + 1 < matrix[0].length) {
queue.offer(new int[] {0, pair[1] + 1});
}
if (pair[0] + 1 < matrix.length) {
queue.offer(new int[] {pair[0] + 1, pair[1]});
}
}
}// O(KlogK)
public List<int[]> kSmallestPairs(int[] nums1, int[] nums2, int k) {
PriorityQueue<int[]> que = new PriorityQueue<>((a,b)->a[0]+a[1]-b[0]-b[1]);
List<int[]> res = new ArrayList<>();
if(nums1.length==0 || nums2.length==0 || k==0) return res;
for(int i=0; i<nums1.length && i<k; i++) que.offer(new int[]{nums1[i], nums2[0], 0});
while(k-- > 0 && !que.isEmpty()){
int[] cur = que.poll();
res.add(new int[]{cur[0], cur[1]});
if(cur[2] == nums2.length-1) continue;
que.offer(new int[]{cur[0],nums2[cur[2]+1], cur[2]+1});
}
return res;
}
public List<int[]> kSmallestPairs(int[] nums1, int[] nums2, int k) {
List<int[]> res = new LinkedList<>();
if(nums1==null || nums1.length==0 || nums2==null || nums2.length==0) {
return res;
}
PriorityQueue<int[]> minQ = new PriorityQueue<>(new Comparator<int[]>(){
public int compare(int[] pair1, int[] pair2) {
return (nums1[pair1[0]]+nums2[pair1[1]])-(nums1[pair2[0]]+nums2[pair2[1]]);
}
});
minQ.offer(new int[]{0, 0});
while (k>0 && !minQ.isEmpty()) {
int[] pair=minQ.poll();
int i = pair[0];
int j = pair[1];
res.add(new int[]{nums1[i], nums2[j]});
k--;
if(j+1<nums2.length) {
minQ.offer(new int[]{i, j+1});
}
if(j==0 && i+1<nums1.length){
minQ.offer(new int[] {i+1, 0});
}
}
return res;
}