-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSolutions.java
More file actions
501 lines (467 loc) Β· 16.5 KB
/
Solutions.java
File metadata and controls
501 lines (467 loc) Β· 16.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
package com.javarena.dsa.datastructures.binaryTree;
import java.util.*;
/**
* Binary Tree Solutions Collection
*
* <p><b>Problem Statement:</b><br>
* Collection of common binary tree problems: identical trees, max depth, zigzag traversal,
* balanced tree check, symmetric tree, path sum, and more.
*
* <p><b>Intuition & Approach:</b><br>
* Common patterns:
* - DFS recursion for tree properties (height, balance, identical)
* - BFS with queue for level-order traversals
* - Deque for zigzag/spiral traversals
* - Path tracking for sum problems
* - Post-order for bottom-up computations
*
* Each method implements a specific tree algorithm with optimal complexity.
*
* <p><b>Time Complexity:</b> Varies by problem, mostly O(N)
* <br><b>Space Complexity:</b> O(H) for recursion, O(N) for BFS
*/
public class Solutions {
/**
* cProblem: Check if two binary trees are identical.
* <p>
* Intuition:
* - Two trees are identical if:
* - Their roots have the same value
* - Their left subtrees are identical
* - Their right subtrees are identical
* <p>
* Time Complexity: O(n) β Traverse each node once
* Space Complexity: O(h) β Call stack for recursion (h = height)
* <p>
*/
public boolean isIdentical(Node r1, Node r2) {
if (r1 == null || r2 == null) return r1 == r2;
if (r1.val != r2.val) return false;
boolean left = isIdentical(r1.left, r2.left);
if (left) {
return isIdentical(r1.right, r2.right);
}
return false;
}
/**
* Problem: Find the maximum depth (height) of a binary tree.
* <p>
* Intuition:
* - The depth of a tree is the longest path from root to leaf.
* - Recursively compute depth of left and right subtrees and return the maximum.
* <p>
* Time Complexity: O(n)
* Space Complexity: O(h)
* <p>
*/
public int maxDepth(Node root) {
if (root == null) return 0;
int left = 1 + maxDepth(root.left);
int right = 1 + maxDepth(root.right);
return Math.max(left, right);
}
/**
* Problem: Zigzag (spiral) level order traversal.
* <p>
* Intuition:
* - Use a deque to support both left-to-right and right-to-left traversal alternately.
* - Add children in reverse order depending on the current direction.
* <p>
* Time Complexity: O(n)
* Space Complexity: O(n)
* <p>
*/
public List<List<Integer>> zigzagLevelOrder(Node root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Deque<Node> q = new LinkedList<>();
q.addFirst(root);
boolean reverse = false;
while (!q.isEmpty()) {
List<Integer> current = new ArrayList<>();
int level = q.size();
for (int i = 0; i < level; i++) {
if (!reverse) {
Node curr = q.pollFirst();
current.add(curr.val);
if (curr.left != null) q.addLast(curr.left);
if (curr.right != null) q.addLast(curr.right);
} else {
Node curr = q.pollLast();
current.add(curr.val);
if (curr.right != null) q.addFirst(curr.right);
if (curr.left != null) q.addFirst(curr.left);
}
}
res.add(current);
reverse = !reverse;
}
return res;
}
/**
* Problem: Validate if a binary tree is a Binary Search Tree (BST).
* <p>
* Intuition:
* - Each node must lie in a valid range: (min, max).
* - For left child: new max = current node value
* - For right child: new min = current node value
* <p>
* Time Complexity: O(n)
* Space Complexity: O(h)
* <p>
*/
public boolean isValidBST(Node root) {
return isValid(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
private boolean isValid(Node root, long lower, long higher) {
if (root == null) return true;
if (!(root.val > lower && root.val < higher)) return false;
boolean left = isValid(root.left, lower, root.val);
if (left) {
return isValid(root.right, root.val, higher);
}
return false;
}
/**
* Problem: Check if two trees are structurally identical and have the same node values.
* <p>
* Intuition:
* - Similar to isIdentical() function, compare node values and recurse on left and right.
* <p>
* Time Complexity: O(n)
* Space Complexity: O(h)
* <p>
*/
public boolean isSameTree(Node p, Node q) {
if (p == null && q == null) return true;
if (p == null || q == null) return false;
if (p.val != q.val) return false;
boolean left = isSameTree(p.left, q.left);
if (left) {
return isSameTree(p.right, q.right);
}
return false;
}
/**
* Determines if a binary tree is height-balanced.
* A binary tree is balanced if the height difference between
* left and right subtrees of every node is no more than 1.
* <p>
* Intuition:
* - Perform post-order traversal (bottom-up).
* - At each node, compute left and right heights.
* - If height difference > 1, propagate `-1` up as a failure flag.
* <p>
* Time Complexity: O(n)
* - Each node is visited once.
* Space Complexity: O(h)
* - Due to recursion stack, where h is the height of the tree.
*/
public boolean isBalanced(Node root) {
if (root == null) return true;
return maxDepth2(root) != -1;
}
/**
* Helper function to compute the depth of the binary tree and
* check for height balance in one traversal.
* <p>
* Intuition:
* - Returns -1 if subtree is unbalanced.
* - Otherwise, returns the actual depth.
* <p>
* Time Complexity: O(n)
* Space Complexity: O(h)
*/
public int maxDepth2(Node root) {
if (root == null) return 0;
int left = maxDepth(root.left);
if (left == -1) return -1;
int right = maxDepth(root.right);
if (right == -1) return -1;
if (Math.abs(right - left) > 1) return -1;
return 1 + Math.max(right, left);
}
/**
* Finds the maximum path sum in a binary tree.
* A path is any sequence of nodes where each pair is connected via parent-child edge.
* It may or may not pass through the root, and must contain at least one node.
* <p>
* Intuition:
* - Use post-order traversal to calculate the max sum from left and right subtrees.
* - For each node, compute the maximum contribution:
* - `max(root + left, root + right, root)` for recursion return.
* - `left + root + right` for updating global max.
* <p>
* Time Complexity: O(n)
* Space Complexity: O(h)
*/
public int maxPathSum(Node root) {
int[] ans = new int[1];
ans[0] = Integer.MIN_VALUE;
maxSum(root, ans);
return ans[0];
}
/**
* Helper function that computes the maximum root-to-leaf path sum and
* updates the overall max path sum.
*/
public int maxSum(Node root, int[] sum) {
if (root == null) return 0;
int l = Math.max(maxSum(root.left, sum), 0);
int r = Math.max(maxSum(root.right, sum), 0);
sum[0] = Math.max(sum[0], l + r + root.val);
return root.val + Math.max(l, r);
}
/**
* Checks whether a binary tree is symmetric around its center.
* <p>
* Intuition:
* A tree is symmetric if its left and right subtrees are mirror images of each other.
* We use recursion to check if the left subtree of one node is a mirror of the right subtree of the other node.
* <p>
* Time Complexity: O(N) β where N is the number of nodes.
* Space Complexity: O(H) β for recursion stack, where H is the height of the tree.
*/
public boolean isSymmetric(Node root) {
return isSymmetric(root, root);
}
private boolean isSymmetric(Node p, Node q) {
if (p == null && q == null) return true;
if (p == null || q == null) return false;
if (p.val == q.val) {
return isSymmetric(p.left, q.right) && isSymmetric(p.right, q.left);
}
return false;
}
/**
* Checks whether the value of each non-leaf node is equal to the sum of its children.
* <p>
* Intuition:
* Perform a DFS traversal.
* At each node, check if `node.val == left.val + right.val`.
* Recursively validate for left and right subtrees.
* <p>
* Time Complexity: O(N)
* Space Complexity: O(H) β where H is the height of the tree (stack space)
*/
public boolean checkTree(Node root) {
if (root == null) return true;
if (root.left == null && root.right == null) return true;
int sum = 0;
if (root.left != null) sum += root.left.val;
if (root.right != null) sum += root.right.val;
if (sum == root.val) {
return checkTree(root.left) && checkTree(root.right);
}
return false;
}
/**
* Return all root-to-leaf paths in a binary tree as strings.
* <p>
* Intuition:
* - Perform a DFS traversal, appending the path as we go.
* - On reaching a leaf node, record the complete path.
* <p>
* Time Complexity: O(n) β visits each node once
* Space Complexity: O(h) β recursion stack, where h = tree height
*/
public List<String> binaryTreePaths(Node root) {
List<String> ans = new ArrayList<>();
if (root == null) return ans;
binaryTreeHelper(root, ans, "");
return ans;
}
private void binaryTreeHelper(Node node, List<String> ans, String str) {
if (node == null) {
return;
}
if (node.left == null && node.right == null) {
ans.add(str + Integer.toString(node.val));
return;
}
String newPath = str + Integer.toString(node.val);
if (node.left != null) {
binaryTreeHelper(node.left, ans, newPath + "->");
}
if (node.right != null) {
binaryTreeHelper(node.right, ans, newPath + "->");
}
}
/**
* Find the maximum width of a binary tree.
* <p>
* Intuition:
* - Use level-order traversal (BFS).
* - Assign position indexes to nodes to simulate full binary tree structure.
* - Width of each level = last index - first index + 1.
* <p>
* Time Complexity: O(n) β each node visited once
* Space Complexity: O(n) β queue stores all nodes in a level
*/
public int widthOfBinaryTree(Node root) {
if (root == null) return 0;
int ans = 0;
Queue<Pair> q = new LinkedList<>();
q.add(new Pair(root, 0));
while (!q.isEmpty()) {
int mmin = q.peek().num;
int last = 0;
int n = q.size();
for (int i = 0; i < n; i++) {
int cur_id = q.peek().num - mmin;
Node node = q.peek().node;
q.poll();
last = cur_id;
if (node.left != null) {
q.add(new Pair(node.left, cur_id * 2 + 1));
}
if (node.right != null) {
q.add(new Pair(node.right, cur_id * 2 + 2));
}
}
ans = Math.max(ans, last + 1);
}
return ans;
}
/**
* Helper method to record parent relationships for all nodes in the binary tree.
* <p>
* Intuition:
* - Perform a level-order traversal (BFS).
* - For every child node, store its parent in a HashMap.
* <p>
* Time Complexity: O(n) β each node is visited once
* Space Complexity: O(n) β to store the parent map and queue
*/
private HashMap<Node, Node> markParents(Node root) {
HashMap<Node, Node> map = new HashMap<>();
Queue<Node> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
Node cur = q.poll();
if (cur.left != null) {
q.offer(cur.left);
map.put(cur.left, cur);
}
if (cur.right != null) {
q.offer(cur.right);
map.put(cur.right, cur);
}
}
return map;
}
/**
* Returns all node values that are exactly k distance away from the target node.
* <p>
* Intuition:
* - First, build a parent map using BFS (to allow upward traversal).
* - Then perform a second BFS starting from the target node.
* - Stop when current level equals k.
* <p>
* Time Complexity: O(n) β every node is visited at most once
* Space Complexity: O(n) β for parent map, visited set, and queue
*/
public List<Integer> distanceK(Node root, Node target, int k) {
HashMap<Node, Node> map = markParents(root);
Set<Node> vis = new HashSet<>();
Queue<Node> q = new LinkedList<>();
int cur = 0;
q.offer(target);
vis.add(target);
while (!q.isEmpty()) {
int n = q.size();
if (cur == k) break;
cur++;
for (int i = 0; i < n; i++) {
Node node = q.poll();
if (node.left != null && !vis.contains(node.left)) {
q.offer(node.left);
vis.add(node.left);
}
if (node.right != null && !vis.contains(node.right)) {
q.offer(node.right);
vis.add(node.right);
}
if (map.get(node) != null && !vis.contains(map.get(node))) {
q.offer(map.get(node));
vis.add(map.get(node));
}
}
}
List<Integer> ans = new ArrayList<>();
while (!q.isEmpty()) {
ans.add(q.poll().val);
}
return ans;
}
/**
* Marks each node's parent in a binary tree and finds the target node with value `start`.
* <p>
* Intuition:
* - To simulate fire spreading to parent nodes, we need a way to move upwards in the tree.
* - Use BFS to traverse the tree and record each nodeβs parent in a map.
* - Simultaneously, identify and return the target node where fire starts.
* <p>
* Time Complexity: O(N) β each node is visited once
* Space Complexity: O(N) β for parent map and BFS queue
*/
private Node markParents(Node root, HashMap<Integer, Node> map, int start) {
Node target = root;
Queue<Node> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
Node cur = q.poll();
if (cur.left != null) {
q.offer(cur.left);
map.put(cur.left.val, cur);
}
if (cur.right != null) {
q.offer(cur.right);
map.put(cur.right.val, cur);
}
if (cur.val == start) target = cur;
}
return target;
}
/**
* Returns the total time to burn the entire binary tree from a given starting node.
* <p>
* Intuition:
* - Model the burning process as a BFS traversal from the starting node.
* - Fire spreads in all three directions: left, right, and parent.
* - Use a set to track visited nodes and count BFS levels to measure time.
* <p>
* Time Complexity: O(N) β each node is visited once
* Space Complexity: O(N) β for queue, visited set, and parent map
*/
public int amountOfTime(Node root, int start) {
HashMap<Integer, Node> map = new HashMap<>();
Node target = markParents(root, map, start);
Set<Node> vis = new HashSet<>();
Queue<Node> q = new LinkedList<>();
int cur = -1;
q.offer(target);
vis.add(target);
while (!q.isEmpty()) {
int n = q.size();
for (int i = 0; i < n; i++) {
Node node = q.poll();
if (node.left != null && !vis.contains(node.left)) {
q.offer(node.left);
vis.add(node.left);
}
if (node.right != null && !vis.contains(node.right)) {
q.offer(node.right);
vis.add(node.right);
}
Node parent = map.get(node.val);
if (parent != null && !vis.contains(parent)) {
q.offer(parent);
vis.add(parent);
}
}
cur++;
}
return cur;
}
}