diff --git a/articles/build-a-matrix-with-conditions.md b/articles/build-a-matrix-with-conditions.md new file mode 100644 index 000000000..8a4d3f610 --- /dev/null +++ b/articles/build-a-matrix-with-conditions.md @@ -0,0 +1,526 @@ +## 1. Topological Sort (DFS) + +::tabs-start + +```python +class Solution: + def buildMatrix(self, k: int, rowConditions: List[List[int]], colConditions: List[List[int]]) -> List[List[int]]: + def dfs(src, adj, visit, path, order): + if src in path: + return False + if src in visit: + return True + visit.add(src) + path.add(src) + for nei in adj[src]: + if not dfs(nei, adj, visit, path, order): + return False + path.remove(src) + order.append(src) + return True + + def topo_sort(edges): + adj = defaultdict(list) + for src, dst in edges: + adj[src].append(dst) + + visit, path = set(), set() + order = [] + for src in range(1, k + 1): + if src not in visit: + if not dfs(src, adj, visit, path, order): + return [] + return order[::-1] + + row_order = topo_sort(rowConditions) + if not row_order: return [] + + col_order = topo_sort(colConditions) + if not col_order: return [] + + val_to_row = {num: i for i, num in enumerate(row_order)} + val_to_col = {num: i for i, num in enumerate(col_order)} + res = [[0] * k for _ in range(k)] + for num in range(1, k + 1): + r, c = val_to_row[num], val_to_col[num] + res[r][c] = num + + return res +``` + +```java +public class Solution { + private Set visit; + private Set path; + private List order; + + public int[][] buildMatrix(int k, int[][] rowConditions, int[][] colConditions) { + int[] rowOrder = topoSort(k, rowConditions); + if (rowOrder == null) return new int[0][0]; + int[] colOrder = topoSort(k, colConditions); + if (colOrder == null) return new int[0][0]; + + Map valToRow = new HashMap<>(); + for (int i = 0; i < rowOrder.length; i++) { + valToRow.put(rowOrder[i], i); + } + Map valToCol = new HashMap<>(); + for (int i = 0; i < colOrder.length; i++) { + valToCol.put(colOrder[i], i); + } + + int[][] res = new int[k][k]; + for (int num = 1; num <= k; num++) { + int r = valToRow.get(num); + int c = valToCol.get(num); + res[r][c] = num; + } + return res; + } + + private int[] topoSort(int k, int[][] edges) { + Map> adj = new HashMap<>(); + for (int i = 1; i <= k; i++) { + adj.put(i, new ArrayList<>()); + } + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + } + + visit = new HashSet<>(); + path = new HashSet<>(); + order = new ArrayList<>(); + + for (int i = 1; i <= k; i++) { + if (!visit.contains(i)) { + if (!dfs(i, adj)) { + return null; + } + } + } + + Collections.reverse(order); + return order.stream().mapToInt(i -> i).toArray(); + } + + private boolean dfs(int src, Map> adj) { + if (path.contains(src)) return false; + if (visit.contains(src)) return true; + + visit.add(src); + path.add(src); + for (int nei : adj.get(src)) { + if (!dfs(nei, adj)) { + return false; + } + } + path.remove(src); + order.add(src); + return true; + } +} +``` + +```cpp +class Solution { +public: + vector> buildMatrix(int k, vector>& rowConditions, vector>& colConditions) { + vector rowOrder = topoSort(k, rowConditions); + if (rowOrder.empty()) return {}; + vector colOrder = topoSort(k, colConditions); + if (colOrder.empty()) return {}; + + unordered_map valToRow, valToCol; + for (int i = 0; i < rowOrder.size(); i++) { + valToRow[rowOrder[i]] = i; + } + for (int i = 0; i < colOrder.size(); i++) { + valToCol[colOrder[i]] = i; + } + + vector> res(k, vector(k, 0)); + for (int num = 1; num <= k; num++) { + int r = valToRow[num]; + int c = valToCol[num]; + res[r][c] = num; + } + return res; + } + +private: + unordered_set visit; + unordered_set path; + vector order; + + vector topoSort(int k, vector>& edges) { + unordered_map> adj; + for (int i = 1; i <= k; i++) { + adj[i] = {}; + } + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + visit.clear(); + path.clear(); + order.clear(); + for (int i = 1; i <= k; i++) { + if (visit.find(i) == visit.end()) { + if (!dfs(i, adj)) { + return {}; + } + } + } + + reverse(order.begin(), order.end()); + return order; + } + + bool dfs(int src, unordered_map>& adj) { + if (path.find(src) != path.end()) return false; + if (visit.find(src) != visit.end()) return true; + + visit.insert(src); + path.insert(src); + for (int nei : adj[src]) { + if (!dfs(nei, adj)) { + return false; + } + } + path.erase(src); + order.push_back(src); + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number[][]} rowConditions + * @param {number[][]} colConditions + * @return {number[][]} + */ + buildMatrix(k, rowConditions, colConditions) { + const rowOrder = this.topoSort(k, rowConditions); + if (!rowOrder) return []; + const colOrder = this.topoSort(k, colConditions); + if (!colOrder) return []; + + const valToRow = {}; + rowOrder.forEach((num, i) => { + valToRow[num] = i; + }); + + const valToCol = {}; + colOrder.forEach((num, i) => { + valToCol[num] = i; + }); + + const res = Array.from({ length: k }, () => Array(k).fill(0)); + for (let num = 1; num <= k; num++) { + const r = valToRow[num]; + const c = valToCol[num]; + res[r][c] = num; + } + return res; + } + + /** + * @param {number} k + * @param {number[][]} edges + * @return {number[]} + */ + topoSort(k, edges) { + const adj = Array.from({ length: k + 1 }, () => []); + edges.forEach(([src, dst]) => { + adj[src].push(dst); + }); + + const visit = new Set(); + const path = new Set(); + const order = []; + + const dfs = (src) => { + if (path.has(src)) return false; + if (visit.has(src)) return true; + + visit.add(src); + path.add(src); + for (const nei of adj[src]) { + if (!dfs(nei)) { + return false; + } + } + path.delete(src); + order.push(src); + return true; + }; + + for (let src = 1; src <= k; src++) { + if (!visit.has(src)) { + if (!dfs(src)) { + return null; + } + } + } + return order.reverse(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ 2 + n + m)$ +* Space complexity: + * $O(k + n + m)$ extra space. + * $O(k ^ 2)$ space for the output matrix. + +> Where $n$ is the size of the array $rowConditions$, $m$ is the size of the array $colConditions$, and $k$ is the size of the output matrix. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def buildMatrix(self, k: int, rowConditions: List[List[int]], colConditions: List[List[int]]) -> List[List[int]]: + def topo_sort(edges): + indegree = [0] * (k + 1) + adj = [[] for _ in range(k + 1)] + for u, v in edges: + adj[u].append(v) + indegree[v] += 1 + + order = [] + q = deque() + for i in range(1, k + 1): + if not indegree[i]: + q.append(i) + + while q: + node = q.popleft() + order.append(node) + for nei in adj[node]: + indegree[nei] -= 1 + if not indegree[nei]: + q.append(nei) + + return order + + row_order = topo_sort(rowConditions) + if len(row_order) != k: return [] + + col_order = topo_sort(colConditions) + if len(col_order) != k: return [] + + res = [[0] * k for _ in range(k)] + colIndex = [0] * (k + 1) + for i in range(k): + colIndex[col_order[i]] = i + + for i in range(k): + res[i][colIndex[row_order[i]]] = row_order[i] + return res +``` + +```java +public class Solution { + public int[][] buildMatrix(int k, int[][] rowConditions, int[][] colConditions) { + int[] rowOrder = topoSort(k, rowConditions); + if (rowOrder.length != k) return new int[0][0]; + + int[] colOrder = topoSort(k, colConditions); + if (colOrder.length != k) return new int[0][0]; + + int[][] res = new int[k][k]; + int[] colIndex = new int[k + 1]; + for (int i = 0; i < k; i++) { + colIndex[colOrder[i]] = i; + } + + for (int i = 0; i < k; i++) { + res[i][colIndex[rowOrder[i]]] = rowOrder[i]; + } + + return res; + } + + private int[] topoSort(int k, int[][] edges) { + int[] indegree = new int[k + 1]; + List> adj = new ArrayList<>(); + for (int i = 0; i <= k; i++) { + adj.add(new ArrayList<>()); + } + + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + indegree[edge[1]]++; + } + + Queue queue = new LinkedList<>(); + int[] order = new int[k]; + int idx = 0; + for (int i = 1; i <= k; i++) { + if (indegree[i] == 0) { + queue.offer(i); + } + } + + while (!queue.isEmpty()) { + int node = queue.poll(); + order[idx++] = node; + for (int nei : adj.get(node)) { + indegree[nei]--; + if (indegree[nei] == 0) { + queue.offer(nei); + } + } + } + + if (idx != k) return new int[0]; + return order; + } +} +``` + +```cpp +class Solution { +public: + vector> buildMatrix(int k, vector>& rowConditions, vector>& colConditions) { + vector rowOrder = topoSort(k, rowConditions); + if (rowOrder.size() != k) return {}; + + vector colOrder = topoSort(k, colConditions); + if (colOrder.size() != k) return {}; + + vector> res(k, vector(k, 0)); + vector colIndex(k + 1); + for (int i = 0; i < k; i++) { + colIndex[colOrder[i]] = i; + } + for (int i = 0; i < k; i++) { + res[i][colIndex[rowOrder[i]]] = rowOrder[i]; + } + return res; + } + +private: + vector topoSort(int k, vector>& edges) { + vector indegree(k + 1, 0); + vector> adj(k + 1); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + indegree[edge[1]]++; + } + + queue q; + vector order; + for (int i = 1; i <= k; i++) { + if (indegree[i] == 0) { + q.push(i); + } + } + + while (!q.empty()) { + int node = q.front(); + q.pop(); + order.push_back(node); + + for (int nei : adj[node]) { + indegree[nei]--; + if (indegree[nei] == 0) { + q.push(nei); + } + } + } + + if (order.size() != k) return {}; + return order; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number[][]} rowConditions + * @param {number[][]} colConditions + * @return {number[][]} + */ + buildMatrix(k, rowConditions, colConditions) { + const rowOrder = this.topoSort(k, rowConditions); + if (rowOrder.length !== k) return []; + + const colOrder = this.topoSort(k, colConditions); + if (colOrder.length !== k) return []; + + const res = Array.from({ length: k }, () => Array(k).fill(0)); + const colIndex = Array(k + 1).fill(0); + + for (let i = 0; i < k; i++) { + colIndex[colOrder[i]] = i; + } + + for (let i = 0; i < k; i++) { + res[i][colIndex[rowOrder[i]]] = rowOrder[i]; + } + + return res; + } + + /** + * @param {number} k + * @param {number[][]} edges + * @return {number[]} + */ + topoSort(k, edges) { + const indegree = Array(k + 1).fill(0); + const adj = Array.from({ length: k + 1 }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + indegree[v]++; + } + + const queue = []; + const order = []; + + for (let i = 1; i <= k; i++) { + if (indegree[i] === 0) { + queue.push(i); + } + } + + while (queue.length) { + const node = queue.shift(); + order.push(node); + for (const nei of adj[node]) { + indegree[nei]--; + if (indegree[nei] === 0) { + queue.push(nei); + } + } + } + + return order.length === k ? order : []; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ 2 + n + m)$ +* Space complexity: + * $O(k + n + m)$ extra space. + * $O(k ^ 2)$ space for the output matrix. + +> Where $n$ is the size of the array $rowConditions$, $m$ is the size of the array $colConditions$, and $k$ is the size of the output matrix. \ No newline at end of file diff --git a/articles/car-pooling.md b/articles/car-pooling.md new file mode 100644 index 000000000..3eac0695d --- /dev/null +++ b/articles/car-pooling.md @@ -0,0 +1,469 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + trips.sort(key=lambda x: x[1]) + + for i in range(len(trips)): + curPass = trips[i][0] + for j in range(i): + if trips[j][2] > trips[i][1]: + curPass += trips[j][0] + if curPass > capacity: + return False + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + Arrays.sort(trips, (a, b) -> Integer.compare(a[1], b[1])); + + for (int i = 0; i < trips.length; i++) { + int curPass = trips[i][0]; + for (int j = 0; j < i; j++) { + if (trips[j][2] > trips[i][1]) { + curPass += trips[j][0]; + } + } + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + sort(trips.begin(), trips.end(), [](const vector& a, const vector& b) { + return a[1] < b[1]; + }); + + for (int i = 0; i < trips.size(); i++) { + int curPass = trips[i][0]; + for (int j = 0; j < i; j++) { + if (trips[j][2] > trips[i][1]) { + curPass += trips[j][0]; + } + } + if (curPass > capacity) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + trips.sort((a, b) => a[1] - b[1]); + + for (let i = 0; i < trips.length; i++) { + let curPass = trips[i][0]; + for (let j = 0; j < i; j++) { + if (trips[j][2] > trips[i][1]) { + curPass += trips[j][0]; + } + } + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + trips.sort(key=lambda t: t[1]) + + minHeap = [] # pair of [end, numPassengers] + curPass = 0 + + for numPass, start, end in trips: + while minHeap and minHeap[0][0] <= start: + curPass -= heapq.heappop(minHeap)[1] + + curPass += numPass + if curPass > capacity: + return False + + heapq.heappush(minHeap, [end, numPass]) + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + Arrays.sort(trips, Comparator.comparingInt(a -> a[1])); + + PriorityQueue minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); // [end, numPassengers] + int curPass = 0; + + for (int[] trip : trips) { + int numPass = trip[0], start = trip[1], end = trip[2]; + + while (!minHeap.isEmpty() && minHeap.peek()[0] <= start) { + curPass -= minHeap.poll()[1]; + } + + curPass += numPass; + if (curPass > capacity) { + return false; + } + + minHeap.offer(new int[]{end, numPass}); + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + sort(trips.begin(), trips.end(), [](const vector& a, const vector& b) { + return a[1] < b[1]; + }); + + priority_queue, vector>, greater<>> minHeap; // [end, numPassengers] + int curPass = 0; + + for (const auto& trip : trips) { + int numPass = trip[0], start = trip[1], end = trip[2]; + + while (!minHeap.empty() && minHeap.top().first <= start) { + curPass -= minHeap.top().second; + minHeap.pop(); + } + + curPass += numPass; + if (curPass > capacity) { + return false; + } + + minHeap.emplace(end, numPass); + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + trips.sort((a, b) => a[1] - b[1]); + + const minHeap = new MinPriorityQueue({ priority: x => x[0] }); // [end, numPassengers] + let curPass = 0; + + for (const [numPass, start, end] of trips) { + while (!minHeap.isEmpty() && minHeap.front().element[0] <= start) { + curPass -= minHeap.dequeue().element[1]; + } + + curPass += numPass; + if (curPass > capacity) { + return false; + } + + minHeap.enqueue([end, numPass]); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Line Sweep - I + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + points = [] + for passengers, start, end in trips: + points.append([start, passengers]) + points.append([end, -passengers]) + + points.sort() + curPass = 0 + for point, passengers in points: + curPass += passengers + if curPass > capacity: + return False + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + List points = new ArrayList<>(); + for (int[] trip : trips) { + int passengers = trip[0], start = trip[1], end = trip[2]; + points.add(new int[]{start, passengers}); + points.add(new int[]{end, -passengers}); + } + + points.sort((a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0])); + + int curPass = 0; + for (int[] point : points) { + curPass += point[1]; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + vector> points; + for (const auto& trip : trips) { + int passengers = trip[0], start = trip[1], end = trip[2]; + points.emplace_back(start, passengers); + points.emplace_back(end, -passengers); + } + + sort(points.begin(), points.end(), [](const pair& a, const pair& b) { + return a.first == b.first ? a.second < b.second : a.first < b.first; + }); + + int curPass = 0; + for (const auto& point : points) { + curPass += point.second; + if (curPass > capacity) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + const points = []; + for (const [passengers, start, end] of trips) { + points.push([start, passengers]); + points.push([end, -passengers]); + } + + points.sort((a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]); + + let curPass = 0; + for (const [point, passengers] of points) { + curPass += passengers; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Line Sweep - II + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + L, R = float("inf"), float("-inf") + for _, start, end in trips: + L = min(L, start) + R = max(R, end) + + N = R - L + 1 + passChange = [0] * (N + 1) + for passengers, start, end in trips: + passChange[start - L] += passengers + passChange[end - L] -= passengers + + curPass = 0 + for change in passChange: + curPass += change + if curPass > capacity: + return False + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + int L = Integer.MAX_VALUE, R = Integer.MIN_VALUE; + for (int[] trip : trips) { + L = Math.min(L, trip[1]); + R = Math.max(R, trip[2]); + } + + int N = R - L + 1; + int[] passChange = new int[N + 1]; + for (int[] trip : trips) { + passChange[trip[1] - L] += trip[0]; + passChange[trip[2] - L] -= trip[0]; + } + + int curPass = 0; + for (int change : passChange) { + curPass += change; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + int L = INT_MAX, R = INT_MIN; + for (const auto& trip : trips) { + L = min(L, trip[1]); + R = max(R, trip[2]); + } + + int N = R - L + 1; + vector passChange(N + 1, 0); + for (const auto& trip : trips) { + passChange[trip[1] - L] += trip[0]; + passChange[trip[2] - L] -= trip[0]; + } + + int curPass = 0; + for (int change : passChange) { + curPass += change; + if (curPass > capacity) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + let L = Infinity, R = -Infinity; + for (const [passengers, start, end] of trips) { + L = Math.min(L, start); + R = Math.max(R, end); + } + + const N = R - L + 1; + const passChange = Array(N + 1).fill(0); + for (const [passengers, start, end] of trips) { + passChange[start - L] += passengers; + passChange[end - L] -= passengers; + } + + let curPass = 0; + for (const change of passChange) { + curPass += change; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + N)$ +* Space complexity: $O(N)$ + +> Where $n$ is the size of the array $trips$ and $N$ is the difference between the rightmost location and the leftmost location. \ No newline at end of file diff --git a/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md b/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md index dd184b948..b20fb8a09 100644 --- a/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md +++ b/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md @@ -578,16 +578,16 @@ class UnionFind { * @param {number} n */ constructor(n) { - this.n = n; this.Parent = Array.from({ length: n + 1 }, (_, i) => i); this.Size = Array(n + 1).fill(1); + this.n = n; } /** - * @param {number} v1 + * @param {number} node * @return {number} */ - find(v1) { + find(node) { if (this.Parent[node] !== node) { this.Parent[node] = this.find(this.Parent[node]); } @@ -595,11 +595,11 @@ class UnionFind { } /** - * @param {number} v1 - * @param {number} v2 + * @param {number} u + * @param {number} v * @return {boolean} */ - union(v1, v2) { + union(u, v) { let pu = this.find(u); let pv = this.find(v); if (pu === pv) return false; @@ -612,6 +612,9 @@ class UnionFind { return true; } + /** + * @return {number} + */ isConnected() { return this.n === 1; } @@ -960,8 +963,6 @@ class Solution: if edges[i][2] == edges[ind][2]: pseudo.add(i) pseudo.add(ind) - mstEdge.add(i) - mstEdge.add(ind) return [list(mstEdge - pseudo), list(pseudo)] ``` diff --git a/articles/greatest-common-divisor-traversal.md b/articles/greatest-common-divisor-traversal.md new file mode 100644 index 000000000..79c11cf70 --- /dev/null +++ b/articles/greatest-common-divisor-traversal.md @@ -0,0 +1,1305 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + n = len(nums) + visit = [False] * n + adj = [[] for _ in range(n)] + for i in range(n): + for j in range(i + 1, n): + if gcd(nums[i], nums[j]) > 1: + adj[i].append(j) + adj[j].append(i) + + def dfs(node): + visit[node] = True + for nei in adj[node]: + if not visit[nei]: + dfs(nei) + + dfs(0) + for node in visit: + if not node: + return False + return True +``` + +```java +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int n = nums.length; + boolean[] visit = new boolean[n]; + List> adj = new ArrayList<>(); + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (gcd(nums[i], nums[j]) > 1) { + adj.get(i).add(j); + adj.get(j).add(i); + } + } + } + + dfs(0, adj, visit); + for (boolean node : visit) { + if (!node) { + return false; + } + } + return true; + } + + private void dfs(int node, List> adj, boolean[] visit) { + visit[node] = true; + for (int nei : adj.get(node)) { + if (!visit[nei]) { + dfs(nei, adj, visit); + } + } + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int n = nums.size(); + vector visit(n, false); + vector> adj(n); + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (__gcd(nums[i], nums[j]) > 1) { + adj[i].push_back(j); + adj[j].push_back(i); + } + } + } + + dfs(0, adj, visit); + for (bool node : visit) { + if (!node) { + return false; + } + } + return true; + } + +private: + void dfs(int node, vector>& adj, vector& visit) { + visit[node] = true; + for (int& nei : adj[node]) { + if (!visit[nei]) { + dfs(nei, adj, visit); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const n = nums.length; + const visit = new Array(n).fill(false); + const adj = Array.from({ length: n }, () => []); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + if (gcd(nums[i], nums[j]) > 1) { + adj[i].push(j); + adj[j].push(i); + } + } + } + + const dfs = (node) => { + visit[node] = true; + for (const nei of adj[node]) { + if (!visit[nei]) { + dfs(nei); + } + } + }; + + dfs(0); + return visit.every((node) => node); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Disjoint Set Union + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.n = n + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + self.n -= 1 + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + def isConnected(self): + return self.n == 1 + +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + uf = UnionFind(len(nums)) + + factor_index = {} # f -> index of value with factor f + for i, num in enumerate(nums): + f = 2 + while f * f <= n: + if n % f == 0: + if f in factor_index: + uf.union(i, factor_index[f]) + else: + factor_index[f] = i + while n % f == 0: + n = n // f + f += 1 + if n > 1: + if n in factor_index: + uf.union(i, factor_index[n]) + else: + factor_index[n] = i + + return uf.isConnected() +``` + +```java +class UnionFind { + private int n; + private int[] Parent; + private int[] Size; + + public UnionFind(int n) { + this.n = n; + this.Parent = new int[n + 1]; + this.Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + this.Parent[i] = i; + this.Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) return false; + n--; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + public boolean isConnected() { + return n == 1; + } +} + +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int n = nums.length; + UnionFind uf = new UnionFind(n); + Map factorIndex = new HashMap<>(); + + for (int i = 0; i < n; i++) { + int num = nums[i]; + int f = 2; + while (f * f <= num) { + if (num % f == 0) { + if (factorIndex.containsKey(f)) { + uf.union(i, factorIndex.get(f)); + } else { + factorIndex.put(f, i); + } + while (num % f == 0) { + num /= f; + } + } + f++; + } + if (num > 1) { + if (factorIndex.containsKey(num)) { + uf.union(i, factorIndex.get(num)); + } else { + factorIndex.put(num, i); + } + } + } + + return uf.isConnected(); + } +} +``` + +```cpp +class UnionFind { +private: + int n; + vector Parent; + vector Size; + +public: + UnionFind(int n) : n(n), Parent(n + 1), Size(n + 1, 1) { + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionNodes(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) return false; + n--; + if (Size[pu] < Size[pv]) swap(pu, pv); + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + bool isConnected() { + return n == 1; + } +}; + +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int n = nums.size(); + UnionFind uf(n); + + unordered_map factor_index; + + for (int i = 0; i < n; i++) { + int num = nums[i]; + int f = 2; + while (f * f <= num) { + if (num % f == 0) { + if (factor_index.count(f)) { + uf.unionNodes(i, factor_index[f]); + } else { + factor_index[f] = i; + } + while (num % f == 0) { + num /= f; + } + } + f++; + } + if (num > 1) { + if (factor_index.count(num)) { + uf.unionNodes(i, factor_index[num]); + } else { + factor_index[num] = i; + } + } + } + + return uf.isConnected(); + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + this.n = n; + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + this.n--; + if (this.Size[pu] < this.Size[pv]) { + [pu, pv] = [pv, pu]; + } + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } + + /** + * @return {number} + */ + isConnected() { + return this.n === 1; + } +} + +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const n = nums.length; + const uf = new UnionFind(n); + const factor_index = new Map(); + + for (let i = 0; i < n; i++) { + let num = nums[i]; + let f = 2; + while (f * f <= num) { + if (num % f === 0) { + if (factor_index.has(f)) { + uf.union(i, factor_index.get(f)); + } else { + factor_index.set(f, i); + } + while (num % f === 0) { + num = Math.floor(num / f); + } + } + f++; + } + if (num > 1) { + if (factor_index.has(num)) { + uf.union(i, factor_index.get(num)); + } else { + factor_index.set(num, i); + } + } + } + + return uf.isConnected(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n\sqrt {m})$ +* Space complexity: $O(n \log m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 3. Sieve of Eratosthenes + DSU + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + N = len(nums) + if N == 1: + return True + if any(num == 1 for num in nums): + return False + + MAX = max(nums) + sieve = [0] * (MAX + 1) + p = 2 + while p * p <= MAX: + if sieve[p] == 0: + for composite in range(p * p, MAX + 1, p): + sieve[composite] = p + p += 1 + + uf = UnionFind(N + MAX + 1) + for i in range(N): + num = nums[i] + if sieve[num] == 0: # num is prime + uf.union(i, N + num) + continue + + while num > 1: + prime = sieve[num] if sieve[num] != 0 else num + uf.union(i, N + prime) + while num % prime == 0: + num //= prime + + root = uf.find(0) + for i in range(1, N): + if uf.find(i) != root: + return False + return True +``` + +```java +class UnionFind { + private int[] Parent; + private int[] Size; + + public UnionFind(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +} + +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int N = nums.length; + if (N == 1) { + return true; + } + int MAX = 0; + for (int num : nums) { + MAX = Math.max(MAX, num); + if (num == 1) { + return false; + } + } + + int[] sieve = new int[MAX + 1]; + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + UnionFind uf = new UnionFind(N + MAX + 1); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + uf.union(i, N + num); + continue; + } + + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + uf.union(i, N + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + int root = uf.find(0); + for (int i = 1; i < N; i++) { + if (uf.find(i) != root) { + return false; + } + } + return true; + } +} +``` + +```cpp +class UnionFind { +private: + vector Parent, Size; + +public: + UnionFind(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSet(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (Size[pu] < Size[pv]) { + swap(pu, pv); + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +}; + +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int N = nums.size(); + if (N == 1) { + return true; + } + for (int num : nums) { + if (num == 1) { + return false; + } + } + + int MAX = *max_element(nums.begin(), nums.end()); + vector sieve(MAX + 1, 0); + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + UnionFind uf(N + MAX + 1); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + uf.unionSet(i, N + num); + continue; + } + + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + uf.unionSet(i, N + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + int root = uf.find(0); + for (int i = 1; i < N; i++) { + if (uf.find(i) != root) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) { + [pu, pv] = [pv, pu]; + } + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } +} + +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const N = nums.length; + if (N === 1) return true; + if (nums.includes(1)) return false; + + const MAX = Math.max(...nums); + const sieve = Array(MAX + 1).fill(0); + for (let p = 2; p * p <= MAX; p++) { + if (sieve[p] === 0) { + for (let composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + const uf = new UnionFind(N + MAX + 1); + for (let i = 0; i < N; i++) { + let num = nums[i]; + if (sieve[num] === 0) { // num is prime + uf.union(i, N + num); + continue; + } + + while (num > 1) { + const prime = sieve[num] !== 0 ? sieve[num] : num; + uf.union(i, N + prime); + while (num % prime === 0) { + num = Math.floor(num / prime); + } + } + } + + const root = uf.find(0); + for (let i = 1; i < N; i++) { + if (uf.find(i) !== root) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n \log m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 4. Sieve of Eratosthenes + DFS + +::tabs-start + +```python +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + N = len(nums) + if N == 1: + return True + if any(num == 1 for num in nums): + return False + + MAX = max(nums) + sieve = [0] * (MAX + 1) + p = 2 + while p * p <= MAX: + if sieve[p] == 0: + for composite in range(p * p, MAX + 1, p): + sieve[composite] = p + p += 1 + + adj = defaultdict(list) + for i in range(N): + num = nums[i] + if sieve[num] == 0: # num is prime + adj[i].append(N + num) + adj[N + num].append(i) + continue + + while num > 1: + prime = sieve[num] if sieve[num] != 0 else num + adj[i].append(N + prime) + adj[N + prime].append(i) + while num % prime == 0: + num //= prime + + visited = set() + def dfs(node): + visited.add(node) + for nei in adj[node]: + if nei not in visited: + dfs(nei) + + dfs(0) + for i in range(N): + if i not in visited: + return False + return True +``` + +```java +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int N = nums.length; + if (N == 1) return true; + for (int num : nums) { + if (num == 1) return false; + } + + int MAX = Arrays.stream(nums).max().getAsInt(); + int[] sieve = new int[MAX + 1]; + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + Map> adj = new HashMap<>(); + for (int i = 0; i < N; i++) { + int num = nums[i]; + adj.putIfAbsent(i, new ArrayList<>()); + + if (sieve[num] == 0) { + adj.putIfAbsent(N + num, new ArrayList<>()); + adj.get(i).add(N + num); + adj.get(N + num).add(i); + continue; + } + + while (num > 1) { + int prime = (sieve[num] == 0) ? num : sieve[num]; + adj.putIfAbsent(N + prime, new ArrayList<>()); + adj.get(i).add(N + prime); + adj.get(N + prime).add(i); + while (num % prime == 0) num /= prime; + } + } + + Set visited = new HashSet<>(); + dfs(0, adj, visited); + for (int i = 0; i < N; i++) { + if (!visited.contains(i)) return false; + } + return true; + } + + private void dfs(int node, Map> adj, Set visited) { + visited.add(node); + for (int neighbor : adj.get(node)) { + if (!visited.contains(neighbor)) { + dfs(neighbor, adj, visited); + } + } + } +} +``` + +```cpp +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int N = nums.size(); + if (N == 1) return true; + if (find(nums.begin(), nums.end(), 1) != nums.end()) return false; + + int MAX = *max_element(nums.begin(), nums.end()); + vector sieve(MAX + 1, 0); + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + unordered_map> adj; + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (!adj.count(i)) adj[i] = {}; + + if (sieve[num] == 0) { + adj[N + num].push_back(i); + adj[i].push_back(N + num); + continue; + } + + while (num > 1) { + int prime = (sieve[num] == 0) ? num : sieve[num]; + adj[N + prime].push_back(i); + adj[i].push_back(N + prime); + while (num % prime == 0) num /= prime; + } + } + + unordered_set visited; + dfs(0, adj, visited); + for (int i = 0; i < N; i++) { + if (visited.find(i) == visited.end()) return false; + } + return true; + } + +private: + void dfs(int node, unordered_map>& adj, unordered_set& visited) { + visited.insert(node); + for (int& neighbor : adj[node]) { + if (visited.find(neighbor) == visited.end()) { + dfs(neighbor, adj, visited); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const N = nums.length; + if (N === 1) return true; + if (nums.includes(1)) return false; + + const MAX = Math.max(...nums); + const sieve = new Array(MAX + 1).fill(0); + for (let p = 2; p * p <= MAX; p++) { + if (sieve[p] === 0) { + for (let composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + const adj = new Map(); + for (let i = 0; i < N; i++) { + if (!adj.has(i)) adj.set(i, []); + let num = nums[i]; + + if (sieve[num] === 0) { + if (!adj.has(N + num)) adj.set(N + num, []); + adj.get(i).push(N + num); + adj.get(N + num).push(i); + continue; + } + + while (num > 1) { + const prime = sieve[num] === 0 ? num : sieve[num]; + if (!adj.has(N + prime)) adj.set(N + prime, []); + adj.get(i).push(N + prime); + adj.get(N + prime).push(i); + while (num % prime === 0) num = Math.floor(num / prime); + } + } + + const visited = new Set(); + const dfs = (node) => { + visited.add(node); + for (const neighbor of adj.get(node) || []) { + if (!visited.has(neighbor)) { + dfs(neighbor); + } + } + }; + + dfs(0); + for (let i = 0; i < N; i++) { + if (!visited.has(i)) return false; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n \log m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 5. Sieve of Eratosthenes + BFS + +::tabs-start + +```python +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + N = len(nums) + if N == 1: + return True + if any(num == 1 for num in nums): + return False + + MAX = max(nums) + sieve = [0] * (MAX + 1) + p = 2 + while p * p <= MAX: + if sieve[p] == 0: + for composite in range(p * p, MAX + 1, p): + sieve[composite] = p + p += 1 + + adj = defaultdict(list) + for i in range(N): + num = nums[i] + if sieve[num] == 0: # num is prime + adj[i].append(N + num) + adj[N + num].append(i) + continue + + while num > 1: + prime = sieve[num] if sieve[num] != 0 else num + adj[i].append(N + prime) + adj[N + prime].append(i) + while num % prime == 0: + num //= prime + + visited = set() + queue = deque([0]) + visited.add(0) + while queue: + node = queue.popleft() + for nei in adj[node]: + if nei not in visited: + visited.add(nei) + queue.append(nei) + + for i in range(N): + if i not in visited: + return False + return True +``` + +```java +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int N = nums.length; + if (N == 1) return true; + for (int num : nums) { + if (num == 1) return false; + } + + int MAX = Arrays.stream(nums).max().getAsInt(); + int[] sieve = new int[MAX + 1]; + int p = 2; + while (p * p <= MAX) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + p++; + } + + Map> adj = new HashMap<>(); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + adj.computeIfAbsent(i, k -> new ArrayList<>()).add(N + num); + adj.computeIfAbsent(N + num, k -> new ArrayList<>()).add(i); + continue; + } + + while (num > 1) { + int prime = (sieve[num] != 0) ? sieve[num] : num; + adj.computeIfAbsent(i, k -> new ArrayList<>()).add(N + prime); + adj.computeIfAbsent(N + prime, k -> new ArrayList<>()).add(i); + while (num % prime == 0) { + num /= prime; + } + } + } + + Set visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + queue.add(0); + visited.add(0); + + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int nei : adj.getOrDefault(node, new ArrayList<>())) { + if (!visited.contains(nei)) { + visited.add(nei); + queue.add(nei); + } + } + } + + for (int i = 0; i < N; i++) { + if (!visited.contains(i)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int N = nums.size(); + if (N == 1) return true; + if (find(nums.begin(), nums.end(), 1) != nums.end()) return false; + + int MAX = *max_element(nums.begin(), nums.end()); + vector sieve(MAX + 1, 0); + int p = 2; + while (p * p <= MAX) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + p++; + } + + unordered_map> adj; + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + adj[i].push_back(N + num); + adj[N + num].push_back(i); + continue; + } + + while (num > 1) { + int prime = (sieve[num] != 0) ? sieve[num] : num; + adj[i].push_back(N + prime); + adj[N + prime].push_back(i); + while (num % prime == 0) { + num /= prime; + } + } + } + + unordered_set visited; + queue q; + q.push(0); + visited.insert(0); + + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (int& nei : adj[node]) { + if (visited.find(nei) == visited.end()) { + visited.insert(nei); + q.push(nei); + } + } + } + + for (int i = 0; i < N; i++) { + if (visited.find(i) == visited.end()) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const N = nums.length; + if (N === 1) return true; + if (nums.includes(1)) return false; + + const MAX = Math.max(...nums); + const sieve = Array(MAX + 1).fill(0); + let p = 2; + while (p * p <= MAX) { + if (sieve[p] === 0) { + for (let composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + p++; + } + + const adj = new Map(); + for (let i = 0; i < N; i++) { + let num = nums[i]; + if (sieve[num] === 0) { // num is prime + if (!adj.has(i)) adj.set(i, []); + if (!adj.has(N + num)) adj.set(N + num, []); + adj.get(i).push(N + num); + adj.get(N + num).push(i); + continue; + } + + while (num > 1) { + const prime = sieve[num] !== 0 ? sieve[num] : num; + if (!adj.has(i)) adj.set(i, []); + if (!adj.has(N + prime)) adj.set(N + prime, []); + adj.get(i).push(N + prime); + adj.get(N + prime).push(i); + while (num % prime === 0) { + num = Math.floor(num / prime); + } + } + } + + const visited = new Set(); + const queue = new Queue([0]); + visited.add(0); + + while (!queue.isEmpty()) { + const node = queue.pop(); + if (adj.has(node)) { + for (const nei of adj.get(node)) { + if (!visited.has(nei)) { + visited.add(nei); + queue.push(nei); + } + } + } + } + + for (let i = 0; i < N; i++) { + if (!visited.has(i)) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n \log m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. \ No newline at end of file diff --git a/articles/ipo.md b/articles/ipo.md new file mode 100644 index 000000000..e71ba4c34 --- /dev/null +++ b/articles/ipo.md @@ -0,0 +1,374 @@ +## 1. Two Heaps + +::tabs-start + +```python +class Solution: + def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int: + maxProfit = [] # Max heap + minCapital = [(c, p) for c, p in zip(capital, profits)] # Min heap + heapq.heapify(minCapital) + + for _ in range(k): + while minCapital and minCapital[0][0] <= w: + c, p = heapq.heappop(minCapital) + heapq.heappush(maxProfit, -p) + + if not maxProfit: + break + + w += -heapq.heappop(maxProfit) + + return w +``` + +```java +public class Solution { + public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) { + PriorityQueue minCapital = new PriorityQueue<>((a, b) -> a[0] - b[0]); // Min heap + PriorityQueue maxProfit = new PriorityQueue<>((a, b) -> b - a); // Max heap + + for (int i = 0; i < capital.length; i++) { + minCapital.offer(new int[]{capital[i], profits[i]}); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.isEmpty() && minCapital.peek()[0] <= w) { + maxProfit.offer(minCapital.poll()[1]); + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.poll(); + } + + return w; + } +} +``` + +```cpp +class Solution { +public: + int findMaximizedCapital(int k, int w, vector& profits, vector& capital) { + priority_queue maxProfit; // Max heap + priority_queue, vector>, greater<>> minCapital; // Min heap + + for (int i = 0; i < capital.size(); i++) { + minCapital.emplace(capital[i], profits[i]); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.empty() && minCapital.top().first <= w) { + maxProfit.push(minCapital.top().second); + minCapital.pop(); + } + if (maxProfit.empty()) { + break; + } + w += maxProfit.top(); + maxProfit.pop(); + } + + return w; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number} w + * @param {number[]} profits + * @param {number[]} capital + * @return {number} + */ + findMaximizedCapital(k, w, profits, capital) { + const minCapital = new MinPriorityQueue({ compare: (a, b) => a[0] - b[0] }); // Min heap + const maxProfit = new MaxPriorityQueue({ compare: (a, b) => b - a }); // Max heap + + for (let i = 0; i < capital.length; i++) { + minCapital.enqueue([capital[i], profits[i]]); + } + + for (let i = 0; i < k; i++) { + while (!minCapital.isEmpty() && minCapital.front()[0] <= w) { + maxProfit.enqueue(minCapital.dequeue()[1]); + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.dequeue(); + } + + return w; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Heaps (Optimal) + +::tabs-start + +```python +class Solution: + def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int: + class Node: + def __init__(self, idx): + self.idx = idx + + def __lt__(self, other): + if capital[self.idx] != capital[other.idx]: + return capital[self.idx] < capital[other.idx] + return self.idx < other.idx + + minCapital = [] + maxProfit = [] + for i in range(len(capital)): + heapq.heappush(minCapital, Node(i)) + + for _ in range(k): + while minCapital and capital[minCapital[0].idx] <= w: + idx = heapq.heappop(minCapital).idx + heapq.heappush(maxProfit, -profits[idx]) + + if not maxProfit: + break + w += -heapq.heappop(maxProfit) + + return w +``` + +```java +public class Solution { + public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) { + PriorityQueue minCapital = new PriorityQueue<>((a, b) -> capital[a] - capital[b]); + PriorityQueue maxProfit = new PriorityQueue<>((a, b) -> profits[b] - profits[a]); + + for (int i = 0; i < capital.length; i++) { + minCapital.offer(i); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.isEmpty() && capital[minCapital.peek()] <= w) { + maxProfit.offer(minCapital.poll()); + } + if (maxProfit.isEmpty()) { + break; + } + w += profits[maxProfit.poll()]; + } + + return w; + } +} +``` + +```cpp +class Solution { +public: + int findMaximizedCapital(int k, int w, vector& profits, vector& capital) { + auto cmpCapital = [&](int a, int b) { return capital[a] > capital[b]; }; + auto cmpProfit = [&](int a, int b) { return profits[a] < profits[b]; }; + + priority_queue, decltype(cmpCapital)> minCapital(cmpCapital); + priority_queue, decltype(cmpProfit)> maxProfit(cmpProfit); + + for (int i = 0; i < capital.size(); i++) { + minCapital.push(i); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.empty() && capital[minCapital.top()] <= w) { + maxProfit.push(minCapital.top()); + minCapital.pop(); + } + if (maxProfit.empty()) { + break; + } + w += profits[maxProfit.top()]; + maxProfit.pop(); + } + + return w; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number} w + * @param {number[]} profits + * @param {number[]} capital + * @return {number} + */ + findMaximizedCapital(k, w, profits, capital) { + const minCapital = new MinPriorityQueue({ + compare: (a, b) => capital[a] - capital[b], + }); + const maxProfit = new MaxPriorityQueue({ + compare: (a, b) => profits[b] - profits[a], + }); + + for (let i = 0; i < capital.length; i++) { + minCapital.enqueue(i); + } + + for (let i = 0; i < k; i++) { + while (!minCapital.isEmpty() && capital[minCapital.front()] <= w) { + maxProfit.enqueue(minCapital.dequeue()); + } + if (maxProfit.isEmpty()) { + break; + } + w += profits[maxProfit.dequeue()]; + } + + return w; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + Max-Heap + +::tabs-start + +```python +class Solution: + def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int: + n = len(profits) + indices = list(range(n)) + indices.sort(key=lambda i: capital[i]) + + maxProfit, idx = [], 0 + for _ in range(k): + while idx < n and capital[indices[idx]] <= w: + heapq.heappush(maxProfit, -profits[indices[idx]]) + idx += 1 + + if not maxProfit: + break + w += -heapq.heappop(maxProfit) + + return w +``` + +```java +public class Solution { + public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) { + int n = profits.length; + Integer[] indices = new Integer[n]; + for (int i = 0; i < n; i++) { + indices[i] = i; + } + Arrays.sort(indices, (a, b) -> Integer.compare(capital[a], capital[b])); + + PriorityQueue maxProfit = new PriorityQueue<>(Collections.reverseOrder()); + int idx = 0; + for (int i = 0; i < k; i++) { + while (idx < n && capital[indices[idx]] <= w) { + maxProfit.add(profits[indices[idx]]); + idx++; + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.poll(); + } + + return w; + } +} +``` + +```cpp +class Solution { +public: + int findMaximizedCapital(int k, int w, vector& profits, vector& capital) { + int n = profits.size(); + vector indices(n); + for (int i = 0; i < n; i++) { + indices[i] = i; + } + sort(indices.begin(), indices.end(), [&](int a, int b) { + return capital[a] < capital[b]; + }); + + priority_queue maxProfit; + int idx = 0; + for (int i = 0; i < k; i++) { + while (idx < n && capital[indices[idx]] <= w) { + maxProfit.push(profits[indices[idx]]); + idx++; + } + if (maxProfit.empty()) { + break; + } + w += maxProfit.top(); + maxProfit.pop(); + } + + return w; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number} w + * @param {number[]} profits + * @param {number[]} capital + * @return {number} + */ + findMaximizedCapital(k, w, profits, capital) { + const n = profits.length; + const indices = Array.from({ length: n }, (_, i) => i); + indices.sort((a, b) => capital[a] - capital[b]); + + const maxProfit = new MaxPriorityQueue(); + let idx = 0; + for (let i = 0; i < k; i++) { + while (idx < n && capital[indices[idx]] <= w) { + maxProfit.enqueue(profits[indices[idx]]); + idx++; + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.dequeue().element; + } + + return w; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/longest-happy-string.md b/articles/longest-happy-string.md new file mode 100644 index 000000000..e5f2e0573 --- /dev/null +++ b/articles/longest-happy-string.md @@ -0,0 +1,458 @@ +## 1. Greedy + +::tabs-start + +```python +class Solution: + def longestDiverseString(self, a: int, b: int, c: int) -> str: + count = [a, b, c] + res = [] + + def getMax(repeated): + idx = -1 + maxCnt = 0 + for i in range(3): + if i == repeated or count[i] == 0: + continue + if maxCnt < count[i]: + maxCnt = count[i] + idx = i + return idx + + repeated = -1 + while True: + maxChar = getMax(repeated) + if maxChar == -1: + break + res.append(chr(maxChar + ord('a'))) + count[maxChar] -= 1 + if len(res) > 1 and res[-1] == res[-2]: + repeated = maxChar + else: + repeated = -1 + + return ''.join(res) +``` + +```java +public class Solution { + public String longestDiverseString(int a, int b, int c) { + int[] count = {a, b, c}; + StringBuilder res = new StringBuilder(); + + int repeated = -1; + while (true) { + int maxChar = getMax(count, repeated); + if (maxChar == -1) { + break; + } + res.append((char) (maxChar + 'a')); + count[maxChar]--; + + if (res.length() > 1 && res.charAt(res.length() - 1) == res.charAt(res.length() - 2)) { + repeated = maxChar; + } else { + repeated = -1; + } + } + + return res.toString(); + } + + private int getMax(int[] count, int repeated) { + int idx = -1, maxCnt = 0; + for (int i = 0; i < 3; i++) { + if (i == repeated || count[i] == 0) { + continue; + } + if (maxCnt < count[i]) { + maxCnt = count[i]; + idx = i; + } + } + return idx; + } +} +``` + +```cpp +class Solution { +public: + string longestDiverseString(int a, int b, int c) { + vector count = {a, b, c}; + string res; + + int repeated = -1; + while (true) { + int maxChar = getMax(count, repeated); + if (maxChar == -1) { + break; + } + res += (char)(maxChar + 'a'); + count[maxChar]--; + + if (res.size() > 1 && res.back() == res[res.size() - 2]) { + repeated = maxChar; + } else { + repeated = -1; + } + } + + return res; + } + +private: + int getMax(const vector& count, int repeated) { + int idx = -1, maxCnt = 0; + for (int i = 0; i < 3; i++) { + if (i == repeated || count[i] == 0) { + continue; + } + if (maxCnt < count[i]) { + maxCnt = count[i]; + idx = i; + } + } + return idx; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @param {number} c + * @return {string} + */ + longestDiverseString(a, b, c) { + const count = [a, b, c]; + const res = []; + + const getMax = (repeated) => { + let idx = -1; + let maxCnt = 0; + for (let i = 0; i < 3; i++) { + if (i === repeated || count[i] === 0) { + continue; + } + if (maxCnt < count[i]) { + maxCnt = count[i]; + idx = i; + } + } + return idx; + }; + + let repeated = -1; + while (true) { + const maxChar = getMax(repeated); + if (maxChar === -1) { + break; + } + res.push(String.fromCharCode(maxChar + 97)); + count[maxChar]--; + + if (res.length > 1 && res[res.length - 1] === res[res.length - 2]) { + repeated = maxChar; + } else { + repeated = -1; + } + } + + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output string. + +--- + +## 2. Greedy (Max-Heap) + +::tabs-start + +```python +class Solution: + def longestDiverseString(self, a: int, b: int, c: int) -> str: + res = "" + maxHeap = [] + for count, char in [(-a, "a"), (-b, "b"), (-c, "c")]: + if count != 0: + heapq.heappush(maxHeap, (count, char)) + + while maxHeap: + count, char = heapq.heappop(maxHeap) + if len(res) > 1 and res[-1] == res[-2] == char: + if not maxHeap: + break + count2, char2 = heapq.heappop(maxHeap) + res += char2 + count2 += 1 + if count2: + heapq.heappush(maxHeap, (count2, char2)) + heapq.heappush(maxHeap, (count, char)) + else: + res += char + count += 1 + if count: + heapq.heappush(maxHeap, (count, char)) + + return res +``` + +```java +public class Solution { + public String longestDiverseString(int a, int b, int c) { + StringBuilder res = new StringBuilder(); + PriorityQueue maxHeap = new PriorityQueue<>((x, y) -> y[0] - x[0]); + + if (a > 0) maxHeap.offer(new int[]{a, 'a'}); + if (b > 0) maxHeap.offer(new int[]{b, 'b'}); + if (c > 0) maxHeap.offer(new int[]{c, 'c'}); + + while (!maxHeap.isEmpty()) { + int[] first = maxHeap.poll(); + if (res.length() > 1 && res.charAt(res.length() - 1) == first[1] && res.charAt(res.length() - 2) == first[1]) { + if (maxHeap.isEmpty()) break; + int[] second = maxHeap.poll(); + res.append((char) second[1]); + second[0]--; + if (second[0] > 0) maxHeap.offer(second); + maxHeap.offer(first); + } else { + res.append((char) first[1]); + first[0]--; + if (first[0] > 0) maxHeap.offer(first); + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string longestDiverseString(int a, int b, int c) { + string res; + priority_queue> maxHeap; + if (a > 0) maxHeap.push({a, 'a'}); + if (b > 0) maxHeap.push({b, 'b'}); + if (c > 0) maxHeap.push({c, 'c'}); + + while (!maxHeap.empty()) { + auto [count, ch] = maxHeap.top(); + maxHeap.pop(); + + if (res.size() > 1 && res[res.size() - 1] == ch && res[res.size() - 2] == ch) { + if (maxHeap.empty()) break; + auto [count2, ch2] = maxHeap.top(); + maxHeap.pop(); + res += ch2; + if (--count2 > 0) maxHeap.push({count2, ch2}); + maxHeap.push({count, ch}); + } else { + res += ch; + if (--count > 0) maxHeap.push({count, ch}); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @param {number} c + * @return {string} + */ + longestDiverseString(a, b, c) { + const res = []; + const maxHeap = new MaxPriorityQueue({ priority: (x) => x[0] }); + + if (a > 0) maxHeap.enqueue([a, 'a']); + if (b > 0) maxHeap.enqueue([b, 'b']); + if (c > 0) maxHeap.enqueue([c, 'c']); + + while (!maxHeap.isEmpty()) { + const [count, char] = maxHeap.dequeue().element; + + if (res.length > 1 && res[res.length - 1] === char && res[res.length - 2] === char) { + if (maxHeap.isEmpty()) break; + const [count2, char2] = maxHeap.dequeue().element; + res.push(char2); + if (count2 - 1 > 0) maxHeap.enqueue([count2 - 1, char2]); + maxHeap.enqueue([count, char]); + } else { + res.push(char); + if (count - 1 > 0) maxHeap.enqueue([count - 1, char]); + } + } + + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output string. + +--- + +## 3. Greedy (Recursion) + +::tabs-start + +```python +class Solution: + def longestDiverseString(self, a: int, b: int, c: int) -> str: + def rec(max1, max2, max3, char1, char2, char3): + if max1 < max2: + return rec(max2, max1, max3, char2, char1, char3) + if max2 < max3: + return rec(max1, max3, max2, char1, char3, char2) + if max2 == 0: + return [char1] * min(2, max1) + + use1 = min(2, max1) + use2 = 1 if max1 - use1 >= max2 else 0 + res = [char1] * use1 + [char2] * use2 + return res + rec(max1 - use1, max2 - use2, max3, char1, char2, char3) + + return ''.join(rec(a, b, c, 'a', 'b', 'c')) +``` + +```java +public class Solution { + public String longestDiverseString(int a, int b, int c) { + return String.join("", rec(a, b, c, 'a', 'b', 'c')); + } + + private List rec(int max1, int max2, int max3, char char1, char char2, char char3) { + if (max1 < max2) { + return rec(max2, max1, max3, char2, char1, char3); + } + if (max2 < max3) { + return rec(max1, max3, max2, char1, char3, char2); + } + if (max2 == 0) { + List result = new ArrayList<>(); + for (int i = 0; i < Math.min(2, max1); i++) { + result.add(String.valueOf(char1)); + } + return result; + } + + int use1 = Math.min(2, max1); + int use2 = (max1 - use1 >= max2) ? 1 : 0; + + List res = new ArrayList<>(); + for (int i = 0; i < use1; i++) { + res.add(String.valueOf(char1)); + } + for (int i = 0; i < use2; i++) { + res.add(String.valueOf(char2)); + } + + res.addAll(rec(max1 - use1, max2 - use2, max3, char1, char2, char3)); + return res; + } +} +``` + +```cpp +class Solution { +public: + string longestDiverseString(int a, int b, int c) { + vector res = rec(a, b, c, 'a', 'b', 'c'); + return string(res.begin(), res.end()); + } + +private: + vector rec(int max1, int max2, int max3, char char1, char char2, char char3) { + if (max1 < max2) { + return rec(max2, max1, max3, char2, char1, char3); + } + if (max2 < max3) { + return rec(max1, max3, max2, char1, char3, char2); + } + if (max2 == 0) { + vector result(min(2, max1), char1); + return result; + } + + int use1 = min(2, max1); + int use2 = (max1 - use1 >= max2) ? 1 : 0; + + vector res(use1, char1); + res.insert(res.end(), use2, char2); + + vector rest = rec(max1 - use1, max2 - use2, max3, char1, char2, char3); + res.insert(res.end(), rest.begin(), rest.end()); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @param {number} c + * @return {string} + */ + longestDiverseString(a, b, c) { + const rec = (max1, max2, max3, char1, char2, char3) => { + if (max1 < max2) { + return rec(max2, max1, max3, char2, char1, char3); + } + if (max2 < max3) { + return rec(max1, max3, max2, char1, char3, char2); + } + if (max2 === 0) { + return Array(Math.min(2, max1)).fill(char1); + } + + const use1 = Math.min(2, max1); + const use2 = max1 - use1 >= max2 ? 1 : 0; + + const res = Array(use1).fill(char1).concat(Array(use2).fill(char2)); + return res.concat(rec(max1 - use1, max2 - use2, max3, char1, char2, char3)); + }; + + return rec(a, b, c, 'a', 'b', 'c').join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ for recursion stack. + * $O(n)$ space for the output string. \ No newline at end of file diff --git a/articles/meeting-rooms-iii.md b/articles/meeting-rooms-iii.md new file mode 100644 index 000000000..d8d33bcdb --- /dev/null +++ b/articles/meeting-rooms-iii.md @@ -0,0 +1,466 @@ +## 1. Sorting + Brute Force + +::tabs-start + +```python +class Solution: + def mostBooked(self, n: int, meetings: List[List[int]]) -> int: + meetings.sort() + rooms = [0] * n # end time of meetings in rooms + meeting_count = [0] * n + + for s, e in meetings: + min_room = 0 + found = False + for i in range(n): + if rooms[i] <= s: + found = True + meeting_count[i] += 1 + rooms[i] = e + break + + if rooms[min_room] > rooms[i]: + min_room = i + if found: + continue + meeting_count[min_room] += 1 + rooms[min_room] += e - s + + return meeting_count.index(max(meeting_count)) +``` + +```java +public class Solution { + public int mostBooked(int n, int[][] meetings) { + Arrays.sort(meetings, (a, b) -> Integer.compare(a[0], b[0])); + long[] rooms = new long[n]; // end times of meetings in rooms + int[] meetingCount = new int[n]; + + for (int[] meeting : meetings) { + int start = meeting[0], end = meeting[1]; + int minRoom = 0; + boolean found = false; + + for (int i = 0; i < n; i++) { + if (rooms[i] <= start) { + meetingCount[i]++; + rooms[i] = end; + found = true; + break; + } + if (rooms[minRoom] > rooms[i]) { + minRoom = i; + } + } + if (found) continue; + + meetingCount[minRoom]++; + rooms[minRoom] += end - start; + } + + int maxIndex = 0; + for (int i = 1; i < n; i++) { + if (meetingCount[i] > meetingCount[maxIndex]) { + maxIndex = i; + } + } + return maxIndex; + } +} +``` + +```cpp +class Solution { +public: + int mostBooked(int n, vector>& meetings) { + sort(meetings.begin(), meetings.end()); + vector rooms(n, 0); // end times of meetings in rooms + vector meetingCount(n, 0); + + for (const auto& meeting : meetings) { + int start = meeting[0], end = meeting[1]; + int minRoom = 0; + bool found = false; + + for (int i = 0; i < n; i++) { + if (rooms[i] <= start) { + meetingCount[i]++; + rooms[i] = end; + found = true; + break; + } + if (rooms[minRoom] > rooms[i]) { + minRoom = i; + } + } + + if (found) continue; + meetingCount[minRoom]++; + rooms[minRoom] += end - start; + } + + int maxIndex = 0; + for (int i = 1; i < n; i++) { + if (meetingCount[i] > meetingCount[maxIndex]) { + maxIndex = i; + } + } + return maxIndex; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @return {number} + */ + mostBooked(n, meetings) { + meetings.sort((a, b) => a[0] - b[0]); + const rooms = new Array(n).fill(0); // end times of meetings in rooms + const meetingCount = new Array(n).fill(0); + + for (const [start, end] of meetings) { + let minRoom = 0; + let found = false; + + for (let i = 0; i < n; i++) { + if (rooms[i] <= start) { + meetingCount[i]++; + rooms[i] = end; + found = true; + break; + } + if (rooms[minRoom] > rooms[i]) { + minRoom = i; + } + } + + if (found) continue; + meetingCount[minRoom]++; + rooms[minRoom] += end - start; + } + + return meetingCount.indexOf(Math.max(...meetingCount)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m\log m + n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of rooms and $m$ is the number of meetings. + +--- + +## 2. Two Min-Heaps + +::tabs-start + +```python +class Solution: + def mostBooked(self, n: int, meetings: List[List[int]]) -> int: + meetings.sort() + available = [i for i in range(n)] # Min heap for available rooms + used = [] # Min heap for used rooms [(end_time, room_number)] + count = [0] * n # Count of meetings for each room + + for start, end in meetings: + while used and used[0][0] <= start: + _, room = heapq.heappop(used) + heapq.heappush(available, room) + + if not available: + end_time, room = heapq.heappop(used) + end = end_time + (end - start) + heapq.heappush(available, room) + + room = heapq.heappop(available) + heapq.heappush(used, (end, room)) + count[room] += 1 + + return count.index(max(count)) +``` + +```java +public class Solution { + public int mostBooked(int n, int[][] meetings) { + Arrays.sort(meetings, (a, b) -> Long.compare(a[0], b[0])); + PriorityQueue available = new PriorityQueue<>(); + PriorityQueue used = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Long.compare(a[1], b[1]) : Long.compare(a[0], b[0]) + ); + for (int i = 0; i < n; i++) { + available.offer(i); + } + int[] count = new int[n]; + + for (int[] meeting : meetings) { + long start = meeting[0]; + long end = meeting[1]; + while (!used.isEmpty() && used.peek()[0] <= start) { + int room = (int) used.poll()[1]; + available.offer(room); + } + if (available.isEmpty()) { + long[] current = used.poll(); + int room = (int) current[1]; + end = current[0] + (end - start); + available.offer(room); + } + + int room = available.poll(); + used.offer(new long[]{end, room}); + count[room]++; + } + + int maxRoom = 0; + for (int i = 1; i < n; i++) { + if (count[i] > count[maxRoom]) { + maxRoom = i; + } + } + return maxRoom; + } +} +``` + +```cpp +class Solution { +public: + int mostBooked(int n, vector>& meetings) { + sort(meetings.begin(), meetings.end(), [](const vector& a, const vector& b) { + return (long long)a[0] < (long long)b[0]; + }); + priority_queue, greater> available; + priority_queue, vector>, greater>> used; + for (int i = 0; i < n; i++) { + available.push(i); + } + vector count(n); + + for (const auto& meeting : meetings) { + long long start = meeting[0]; + long long end = meeting[1]; + while (!used.empty() && used.top().first <= start) { + int room = used.top().second; + used.pop(); + available.push(room); + } + if (available.empty()) { + auto current = used.top(); + used.pop(); + end = current.first + (end - start); + available.push(current.second); + } + + int room = available.top(); + available.pop(); + used.push({end, room}); + count[room]++; + } + + int maxRoom = 0; + for (int i = 1; i < n; i++) { + if (count[i] > count[maxRoom]) { + maxRoom = i; + } + } + return maxRoom; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @return {number} + */ + mostBooked(n, meetings) { + meetings.sort((a, b) => a[0] - b[0]); + const available = new MinPriorityQueue({ compare: (a, b) => a - b }); + const used = new MinPriorityQueue({ + compare: (a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]), + }); + for (let i = 0; i < n; i++) { + available.enqueue(i); + } + const count = new Array(n).fill(0); + + for (const [start, end] of meetings) { + while (!used.isEmpty() && used.front()[0] <= start) { + const room = used.dequeue()[1]; + available.enqueue(room); + } + + let room; + let newEnd = end; + if (available.isEmpty()) { + const [endTime, usedRoom] = used.dequeue(); + newEnd = endTime + (end - start); + available.enqueue(usedRoom); + } + room = available.dequeue(); + used.enqueue([newEnd, room]); + count[room]++; + } + + return count.indexOf(Math.max(...count)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m\log m + m \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of rooms and $m$ is the number of meetings. + +--- + +## 3. One Min-Heap + +::tabs-start + +```python +class Solution: + def mostBooked(self, n: int, meetings: List[List[int]]) -> int: + meetings.sort() + available = [] + count = [0] * n + + for i in range(n): + heapq.heappush(available, (0, i)) + + for start, end in meetings: + while available and available[0][0] < start: + end_time, room = heapq.heappop(available) + heapq.heappush(available, (start, room)) + + end_time, room = heapq.heappop(available) + heapq.heappush(available, (end_time + (end - start), room)) + count[room] += 1 + + return count.index(max(count)) +``` + +```java +public class Solution { + public int mostBooked(int n, int[][] meetings) { + Arrays.sort(meetings, (a, b) -> Integer.compare(a[0], b[0])); + PriorityQueue available = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Long.compare(a[1], b[1]) : Long.compare(a[0], b[0]) + ); + for (int i = 0; i < n; i++) { + available.offer(new long[]{0, i}); + } + int[] count = new int[n]; + + for (int[] meeting : meetings) { + int start = meeting[0], end = meeting[1]; + while (!available.isEmpty() && available.peek()[0] < start) { + long[] earliest = available.poll(); + available.offer(new long[]{start, earliest[1]}); + } + + long[] room = available.poll(); + long endTime = room[0] + (end - start); + available.offer(new long[]{endTime, room[1]}); + count[(int) room[1]]++; + } + + int maxRoom = 0; + for (int i = 1; i < n; i++) { + if (count[i] > count[maxRoom]) { + maxRoom = i; + } + } + return maxRoom; + } +} +``` + +```cpp +class Solution { +public: + int mostBooked(int n, vector>& meetings) { + sort(meetings.begin(), meetings.end()); + priority_queue, vector>, greater>> available; + for (int i = 0; i < n; i++) { + available.push({0, i}); + } + vector count(n); + + for (const auto& meeting : meetings) { + int start = meeting[0], end = meeting[1]; + while (!available.empty() && available.top().first < start) { + auto [end_time, room] = available.top(); + available.pop(); + available.push({start, room}); + } + + auto [end_time, room] = available.top(); + available.pop(); + available.push({end_time + (end - start), room}); + count[room]++; + } + + return max_element(count.begin(), count.end()) - count.begin(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @return {number} + */ + mostBooked(n, meetings) { + meetings.sort((a, b) => a[0] - b[0]); + const available = new MinPriorityQueue({ + compare: (a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]) + }); + for (let i = 0; i < n; i++) { + available.enqueue([0, i]); + } + const count = new Array(n).fill(0); + + for (const [start, end] of meetings) { + while (!available.isEmpty() && available.front()[0] < start) { + const [endTime, room] = available.dequeue(); + available.enqueue([start, room]); + } + + const [endTime, room] = available.dequeue(); + available.enqueue([endTime + (end - start), room]); + count[room]++; + } + + return count.indexOf(Math.max(...count)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(m \log m + m \log n)$ time in average case. + * $O(m \log m + m * n)$ time in worst case. +* Space complexity: $O(n)$ + +> Where $n$ is the number of rooms and $m$ is the number of meetings. \ No newline at end of file diff --git a/articles/single-threaded-cpu.md b/articles/single-threaded-cpu.md new file mode 100644 index 000000000..5b7005989 --- /dev/null +++ b/articles/single-threaded-cpu.md @@ -0,0 +1,471 @@ +## 1. Two Min-Heaps + +::tabs-start + +```python +class Solution: + def getOrder(self, tasks: List[List[int]]) -> List[int]: + available = [] + pending = [] + for i, (enqueueTime, processTime) in enumerate(tasks): + heapq.heappush(pending, (enqueueTime, processTime, i)) + + time = 0 + res = [] + while pending or available: + while pending and pending[0][0] <= time: + enqueueTime, processTime, i = heapq.heappop(pending) + heapq.heappush(available, (processTime, i)) + + if not available: + time = pending[0][0] + continue + + processTime, i = heapq.heappop(available) + time += processTime + res.append(i) + + return res +``` + +```java +public class Solution { + public int[] getOrder(int[][] tasks) { + PriorityQueue available = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]) + ); + PriorityQueue pending = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); + + int n = tasks.length; + for (int i = 0; i < n; i++) { + pending.offer(new int[]{tasks[i][0], tasks[i][1], i}); + } + + long time = 0; + int idx = 0; + int[] res = new int[n]; + while (!pending.isEmpty() || !available.isEmpty()) { + while (!pending.isEmpty() && pending.peek()[0] <= time) { + int[] task = pending.poll(); + available.offer(new int[]{task[1], task[2]}); + } + + if (available.isEmpty()) { + time = pending.peek()[0]; + continue; + } + + int[] task = available.poll(); + time += task[0]; + res[idx++] = task[1]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getOrder(vector>& tasks) { + priority_queue, vector>, greater<>> available; + priority_queue, vector>, greater<>> pending; + + int n = tasks.size(); + for (int i = 0; i < n; ++i) { + pending.push({tasks[i][0], tasks[i][1], i}); + } + + vector res; + long long time = 0; + while (!pending.empty() || !available.empty()) { + while (!pending.empty() && pending.top()[0] <= time) { + auto [enqueueTime, processTime, index] = pending.top(); + pending.pop(); + available.push({processTime, index}); + } + + if (available.empty()) { + time = pending.top()[0]; + continue; + } + + auto [processTime, index] = available.top(); + available.pop(); + time += processTime; + res.push_back(index); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} tasks + * @return {number[]} + */ + getOrder(tasks) { + const available = new MinPriorityQueue({ + compare: (a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] + }); + const pending = new MinPriorityQueue({ + compare: (a, b) => a[0] - b[0] + }); + + tasks.forEach(([enqueueTime, processTime], i) => { + pending.enqueue([enqueueTime, processTime, i]); + }); + + let time = 0; + const res = []; + while (!pending.isEmpty() || !available.isEmpty()) { + while (!pending.isEmpty() && pending.front()[0] <= time) { + const [enqueueTime, processTime, i] = pending.dequeue(); + available.enqueue([processTime, i]); + } + + if (available.isEmpty()) { + time = pending.front()[0]; + continue; + } + + const [processTime, i] = available.dequeue(); + time += processTime; + res.push(i); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting + Min-Heap + +::tabs-start + +```python +class Solution: + def getOrder(self, tasks: List[List[int]]) -> List[int]: + for i, t in enumerate(tasks): + t.append(i) + tasks.sort(key=lambda t: t[0]) + + res, minHeap = [], [] + i, time = 0, tasks[0][0] + + while minHeap or i < len(tasks): + while i < len(tasks) and time >= tasks[i][0]: + heapq.heappush(minHeap, [tasks[i][1], tasks[i][2]]) + i += 1 + if not minHeap: + time = tasks[i][0] + else: + procTime, index = heapq.heappop(minHeap) + time += procTime + res.append(index) + return res +``` + +```java +public class Solution { + public int[] getOrder(int[][] tasks) { + int n = tasks.length; + for (int i = 0; i < n; i++) { + tasks[i] = new int[] {tasks[i][0], tasks[i][1], i}; + } + Arrays.sort(tasks, Comparator.comparingInt(t -> t[0])); + + int[] res = new int[n]; + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]) + ); + + int i = 0, idx = 0; + long time = tasks[0][0]; + while (!minHeap.isEmpty() || i < n) { + while (i < n && time >= tasks[i][0]) { + minHeap.offer(new int[]{tasks[i][1], tasks[i][2]}); + i++; + } + if (minHeap.isEmpty()) { + time = tasks[i][0]; + } else { + int[] task = minHeap.poll(); + time += task[0]; + res[idx++] = task[1]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getOrder(vector>& tasks) { + int n = tasks.size(); + for (int i = 0; i < n; ++i) { + tasks[i].push_back(i); + } + sort(tasks.begin(), tasks.end()); + + vector res; + priority_queue, vector>, greater<>> minHeap; + + int i = 0; + long long time = tasks[0][0]; + while (!minHeap.empty() || i < n) { + while (i < n && time >= tasks[i][0]) { + minHeap.push({tasks[i][1], tasks[i][2]}); + i++; + } + if (minHeap.empty()) { + time = tasks[i][0]; + } else { + auto [procTime, index] = minHeap.top(); + minHeap.pop(); + time += procTime; + res.push_back(index); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} tasks + * @return {number[]} + */ + getOrder(tasks) { + const n = tasks.length; + tasks = tasks.map((t, i) => [...t, i]); + tasks.sort((a, b) => a[0] - b[0]); + + const res = []; + const minHeap = new MinPriorityQueue({ compare: (a, b) => + a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] + }); + + let i = 0, time = tasks[0][0]; + while (minHeap.size() || i < n) { + while (i < n && time >= tasks[i][0]) { + minHeap.enqueue([tasks[i][1], tasks[i][2]]); + i++; + } + if (minHeap.isEmpty()) { + time = tasks[i][0]; + } else { + const [procTime, index] = minHeap.dequeue(); + time += procTime; + res.push(index); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + Min-Heap (Optimal) + +::tabs-start + +```python +class Solution: + def getOrder(self, tasks: List[List[int]]) -> List[int]: + n = len(tasks) + indices = list(range(n)) + indices.sort(key=lambda i: (tasks[i][0], i)) + + class Task: + def __init__(self, idx): + self.idx = idx + + def __lt__(self, other): + if tasks[self.idx][1] != tasks[other.idx][1]: + return tasks[self.idx][1] < tasks[other.idx][1] + return self.idx < other.idx + + minHeap = [] + res = [] + time = i = 0 + while minHeap or i < n: + while i < n and tasks[indices[i]][0] <= time: + heapq.heappush(minHeap, Task(indices[i])) + i += 1 + + if not minHeap: + time = tasks[indices[i]][0] + else: + next_task = heapq.heappop(minHeap) + time += tasks[next_task.idx][1] + res.append(next_task.idx) + + return res +``` + +```java +public class Solution { + public int[] getOrder(int[][] tasks) { + int n = tasks.length; + Integer[] indices = new Integer[n]; + for (int i = 0; i < n; i++) { + indices[i] = i; + } + + Arrays.sort(indices, (a, b) -> + tasks[a][0] != tasks[b][0] ? tasks[a][0] - tasks[b][0] : a - b + ); + + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> + tasks[a][1] != tasks[b][1] ? tasks[a][1] - tasks[b][1] : a - b + ); + + int[] result = new int[n]; + long time = 0; + int i = 0, resIndex = 0; + while (!minHeap.isEmpty() || i < n) { + while (i < n && tasks[indices[i]][0] <= time) { + minHeap.offer(indices[i]); + i++; + } + + if (minHeap.isEmpty()) { + time = tasks[indices[i]][0]; + } else { + int nextIndex = minHeap.poll(); + time += tasks[nextIndex][1]; + result[resIndex++] = nextIndex; + } + } + + return result; + } +} +``` + +```cpp +// C++ Solution +class Solution { +public: + vector getOrder(vector>& tasks) { + int n = tasks.size(); + vector indices(n); + iota(indices.begin(), indices.end(), 0); + + sort(indices.begin(), indices.end(), [&](int a, int b) { + return tasks[a][0] < tasks[b][0] || + (tasks[a][0] == tasks[b][0] && a < b); + }); + + auto comp = [&](int a, int b) { + return tasks[a][1] > tasks[b][1] || + (tasks[a][1] == tasks[b][1] && a > b); + }; + priority_queue, decltype(comp)> minHeap(comp); + + vector result; + long long time = 0; + int i = 0; + + while (!minHeap.empty() || i < n) { + while (i < n && tasks[indices[i]][0] <= time) { + minHeap.push(indices[i]); + i++; + } + + if (minHeap.empty()) { + time = tasks[indices[i]][0]; + } else { + int nextIndex = minHeap.top(); + minHeap.pop(); + time += tasks[nextIndex][1]; + result.push_back(nextIndex); + } + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} tasks + * @return {number[]} + */ + getOrder(tasks) { + const n = tasks.length; + const indices = Array.from({ length: n }, (_, i) => i); + indices.sort((a, b) => { + if (tasks[a][0] !== tasks[b][0]) { + return tasks[a][0] - tasks[b][0]; + } + return a - b; + }); + + const minHeap = new MinPriorityQueue({ + compare: (a, b) => { + if (tasks[a][1] !== tasks[b][1]) { + return tasks[a][1] - tasks[b][1]; + } + return a - b; + } + }); + + const res = []; + let time = 0; + let i = 0; + + while (!minHeap.isEmpty() || i < n) { + while (i < n && tasks[indices[i]][0] <= time) { + minHeap.enqueue(indices[i]); + i++; + } + + if (minHeap.size() === 0) { + time = tasks[indices[i]][0]; + } else { + const nextIndex = minHeap.dequeue(); + time += tasks[nextIndex][1]; + res.push(nextIndex); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file