Irbisa · Cheatsheet
Data Structures & Algo
Arrays, trees, graphs, sorting, searching, Big O notation
What is Big O notation, and what do common operations on Dart's
List,MapandSetcost?What are a stack and a queue, and which Dart classes do you use for each?
import 'dart:collection'; // Stack: a plain List, push = add, pop = removeLastbool isBalanced(String s) { const pairs = {')': '(', ']': '[', '}': '{'}; final stack = <String>[];…How does a
SetorMapturn duplicate detection, an anagram check and two-sum from O(n²) into O(n)?bool hasDuplicate(List<int> xs) { final seen = <int>{}; for (final x in xs) { if (!seen.add(x)) return true; // add returns false if already present } return false;…When would a linked list beat Dart's
List, and what does it cost you?Dart's
{}is already aLinkedHashMap, so when would you pickHashMap,SplayTreeMapor a sortedListinstead?Which sorting algorithms should you know, what do they cost, and what does Dart's
List.sortuse?Sort users by score descending, then by name: how do you write the comparator, and what does Dart's
List.sortguarantee?import 'package:collection/collection.dart'; class User { const User(this.name, this.score); final String name; final int score;…You shuffle a quiz deck with
sort((a, b) => Random().nextInt(3) - 1)and the order comes out biased: what is the correct algorithm?import 'dart:math'; // wrong: a random comparator breaks the sort contract and is biased// questions.sort((a, b) => Random().nextInt(3) - 1); // right: List.shuffle is Fisher-Yates, O(n) and uniform…This function should return the index of the first timestamp
>= target, but it hangs on some inputs: what is wrong, and how do you fix it?int firstAtLeast(List<int> a, int target) { var lo = 0, hi = a.length; while (lo < hi) { final mid = (lo + hi) ~/ 2; if (a[mid] < target) { lo = mid; } else { hi = mid; } } return lo;}How do the two-pointer and sliding-window techniques turn an O(n²) scan into O(n)?
import 'dart:math'; // Two pointers on a sorted list: a pair with the given sum, O(n)(int, int)? pairWithSum(List<int> sorted, int target) { var l = 0, r = sorted.length - 1; while (l < r) {…A stats screen lets the user pick any date range over 5,000 daily totals and re-sums the range on every drag: what makes each query O(1)?
// Build once, O(n); the extra leading 0 removes the l == 0 special caseList<int> buildPrefix(List<int> values) { final prefix = List<int>.filled(values.length + 1, 0); for (var i = 0; i < values.length; i++) { prefix[i + 1] = prefix[i] + values[i]; }…Reverse a string in Dart. Why does the obvious one-liner break on emoji, and what is the correct version?
import 'package:characters/characters.dart'; // ships with Flutter String reverseBroken(String s) => s.split('').reversed.join(); // wrong: code unitsString reverseRunes(String s) => String.fromCharCodes(s.runes.toList().reversed); // still breaks flags, accentsString reverse(String s) => s.characters.toList().reversed.join(); // right…When does recursion overflow the stack in Dart, and how does memoization fix a naive recursive Fibonacci?
// O(2^n): fib(40) makes over 300 million callsint fibNaive(int n) => n < 2 ? n : fibNaive(n - 1) + fibNaive(n - 2); // O(n) with a memo, but still n frames deep on the first callint fibMemo(int n, [Map<int, int>? memo]) { memo ??= {};…Find the bug: this memoized grid-path counter returns the right number, but a 14×14 grid already takes over ten seconds.
/// Paths from the top-left to the bottom-right cell, moving right or down; '#' is a wall.int countPaths(List<String> grid) { final memo = <List<int>, int>{}; int go(int r, int c) { if (r >= grid.length || c >= grid[0].length || grid[r][c] == '#') return 0; if (r == grid.length - 1 && c == grid[0].length - 1) return 1; final key = [r, c]; final cached = memo[key]; if (cached != null) return cached; return memo[key] = go(r + 1, c) + go(r, c + 1); } return go(0, 0);}How do you traverse a binary tree with DFS and BFS, and which order fits which problem?
import 'dart:collection'; class TreeNode { TreeNode(this.val, [this.left, this.right]); final int val; final TreeNode? left, right;…This BFS returns the right order, but on a 100,000-node graph it takes seconds: what makes it quadratic?
List<int> bfsOrder(Map<int, List<int>> graph, int start) { final queue = [start]; final visited = [start]; final order = <int>[]; while (queue.isNotEmpty) { final node = queue.removeAt(0); order.add(node); for (final next in graph[node] ?? const <int>[]) { if (!visited.contains(next)) { visited.add(next); queue.add(next); } } } return order;}Find the bug: this check reports a dependency cycle among startup tasks that have none.
/// Returns true if the task dependency graph contains a cycle.bool hasCycle(Map<String, List<String>> deps) { final visited = <String>{}; bool visit(String task) { if (visited.contains(task)) return true; visited.add(task); for (final dep in deps[task] ?? const <String>[]) { if (visit(dep)) return true; } return false; } for (final task in deps.keys) { if (!visited.contains(task) && visit(task)) return true; } return false;}Which graph algorithms come up in interviews (BFS shortest path, Dijkstra, topological sort), and when do you use each?
import 'package:collection/collection.dart'; /// Shortest distance from [start] to every reachable node; weights must be >= 0.Map<String, int> dijkstra( Map<String, List<(String, int)>> graph, String start) { final dist = <String, int>{start: 0};…How do you recognise that a problem is dynamic programming, and how do you go from plain recursion to DP?
import 'dart:math'; // Top-down: the plain recursion plus a memo keyed by the state (amount left)int minCoinsMemo(List<int> coins, int amount) { final memo = <int, int>{}; int solve(int rem) {…This combination-sum generator returns the right number of results, but every one of them is empty: why, and how would you prune it?
List<List<int>> combinations(List<int> nums, int target) { final results = <List<int>>[], path = <int>[]; void backtrack(int start, int remaining) { if (remaining == 0) { results.add(path); return; } for (var i = start; i < nums.length; i++) { path.add(nums[i]); backtrack(i + 1, remaining - nums[i]); path.removeLast(); } } backtrack(0, target); return results;}Ten million events stream in and you need the 100 highest scores. What structure do you use, and what does it cost?
import 'package:collection/collection.dart'; typedef Event = ({String id, int score}); Future<List<Event>> topK(Stream<Event> events, int k) async { if (k <= 0) return const [];…An image cache has to evict the least-recently-used entry in O(1): how do you build it in Dart, and what does the default
Mapalready give you?class LruCache<K, V> { LruCache(this.capacity); final int capacity; // Default Map is a LinkedHashMap: keys.first is the least recently used final _entries = <K, V>{};…Merging duplicate contacts: after a million merges, "are these two records the same person?" has to answer instantly, so what structure do you reach for?
class DisjointSet<T> { final _parent = <T, T>{}; final _size = <T, int>{}; T find(T x) { _parent.putIfAbsent(x, () => x);…Autocomplete over 80,000 city names re-runs
startsWithon every keystroke and the field stutters: what do you build instead?import 'package:characters/characters.dart'; // ships with Flutter class TrieNode { final children = <String, TrieNode>{}; final top = <String>[]; // best few completions, filled at index time}…