Skip to content

Commit 28346a4

Browse files
committedSep 2, 2020
feat(book/binary-tree): add right side view exercise and solution
1 parent 068dd9c commit 28346a4

File tree

9 files changed

+236
-111
lines changed

9 files changed

+236
-111
lines changed
 

‎book/D-interview-questions-solutions.asc

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ The stack contains the indexes rather than the temperatures themselves.
275275
[#queue-q-recent-counter]
276276
include::content/part02/queue.asc[tag=queue-q-recent-counter]
277277

278-
We are asked to keep track of the request's count only within a given time window. A queue is a perfect application for this. We can add any new request to the Queue. Also, we need to check if the oldest element is outside the time window. If so, we remove it from the queue.
278+
We are asked to keep track of the request's count only within a given time window. A queue is a perfect application for this. We can add any new request to the Queue. Also, we need to check if the oldest element is outside the time window. If so, we remove it from the queue.
279279

280280
*Algorithm*:
281281

@@ -327,7 +327,7 @@ As you can see, we opted for using a set to trade speed for memory.
327327

328328
*Complexity Analysis*:
329329

330-
- Time: `O(1)`. Insert/Remove from Queue is constant time. Check for body collisions is `O(1)` when using a set. If instead of a set, you traversed the snake's queue to find a collision, it would be `O(n)`. Here`n` is the snake's max length, which is the size of the screen (height x width).
330+
- Time: `O(1)`. Insert/Remove from Queue is constant time. Check for body collisions is `O(1)` when using a set. If instead of a set, you traversed the snake's queue to find a collision, it would be `O(n)`. Here`n` is the snake's max length, which is the screen size (height x width).
331331
- Space: `O(n + m)`. `m` is the number of food items, and `n` is the snake's maximum size (height x width).
332332

333333

@@ -342,7 +342,7 @@ As you can see, we opted for using a set to trade speed for memory.
342342
[#binary-tree-q-diameter-of-binary-tree]
343343
include::content/part03/tree-intro.asc[tag=binary-tree-q-diameter-of-binary-tree]
344344

345-
We are asked to find the longest path on binary tree that might or might not pass through the root node.
345+
We are asked to find the longest path on a binary tree that might or might not pass through the root node.
346346

347347
We can calculate the height (distance from root to farthest leaf) of a binary tree using this recursive function:
348348

@@ -356,14 +356,14 @@ function getHeight(node) {
356356
}
357357
----
358358

359-
That will give use the height from furthest leaf to root. However, the problem says that it might or might not go through the root.
360-
In that case, we can keep track of the maximun distance (`leftHeight + rightHeight`) seen so far.
359+
That function will give us the height from the furthest leaf to the root. However, the problem says that it might or might not go through the root.
360+
In that case, we can keep track of the maximum distance (`leftHeight + rightHeight`) seen so far.
361361

362362
*Algorithm*:
363363

364364
- Initialize diameter to `0`
365-
- Recursively find the height of the tree from root.
366-
- Keep track of the maximun diameter length seen so far (left + right).
365+
- Recursively find the height of the tree from the root.
366+
- Keep track of the maximum diameter length seen so far (left + right).
367367
- Return the diameter.
368368

369369
*Implementation*:
@@ -377,38 +377,53 @@ We are using `Math.max` to keep track of the longest diameter seen.
377377

378378
*Complexity Analysis*:
379379

380-
- Time: `O(n)`, where `n` is each one of the nodes on the tree. We visite each one once.
381-
- Space: `O(n)`. We use `O(1)` variables, but because we are using the `height` recursive function we are using the implicit call stack, thus `O(n)`.
380+
- Time: `O(n)`, where `n` is each of the tree nodes. We visite each one once.
381+
- Space: `O(n)`. We use `O(1)` variables, but because we are using the `height` recursive function, we use the implicit call stack, thus `O(n)`.
382382

383383

384384

385385

386-
[#binary-tree-q-diameter-of-binary-tree2]
387-
include::content/part03/tree-intro.asc[tag=binary-tree-q-diameter-of-binary-tree]
386+
[#binary-tree-q-binary-tree-right-side-view]
387+
include::content/part03/tree-intro.asc[tag=binary-tree-q-binary-tree-right-side-view]
388+
389+
The first thing that might come to mind when you have to visit a tree, level by level, is BFS.
390+
We can visit the tree using a Queue and keep track when a level ends, and the new one starts.
388391

389-
RESTATE REQUIREMENTS AND DESCRIPTIONS
392+
Since during BFS, we dequeue one node and enqueue their two children (left and right), we might have two levels (current and next one). For this problem, we need to know what the last node on the current level is.
393+
394+
.There are several ways to solve this problem using BFS. Here are some ideas:
395+
- *1 Queue + Sentinel node*: we can use a special character in the `Queue` like `'*'` or `null` to indicate the level's change. So, we would start something like this `const queue = new Queue([root, '*']);`.
396+
- *2 Queues*: using a "special" character might be seen as hacky, so you can also opt to keep two queues: one for the current level and another for the next level.
397+
- *1 Queue + size tracking*: we track the Queue's `size` before the children are enqueued. That way, we know where the current level ends. We are going to implement this one.
390398

391399
*Algorithm*:
392400

393-
- STEP 1
394-
- STEP 2
395-
- STEP 2.1
396-
- STEP 2.2
401+
- Enqueue root
402+
- While the queue has an element
403+
- Check the current size of the queue
404+
- Dequeue only `size` times, and for each dequeued node, enqueue their children.
405+
- Check if the node is the last one in its level and add it to the answer.
397406

398407
*Implementation*:
399408

400409
[source, javascript]
401410
----
402-
include::interview-questions/diameter-of-binary-tree.js[tag=description]
403-
include::interview-questions/diameter-of-binary-tree.js[tag=solution]
411+
include::interview-questions/binary-tree-right-side-view.js[tags=description;solution]
412+
----
413+
414+
This problem is also possible to be solved using DFS. The trick is to start with the right child and add it to the solution when it is the first one on its level.
415+
416+
[source, javascript]
417+
----
418+
include::interview-questions/binary-tree-right-side-view.js[tag=dfs]
404419
----
405420

406-
IMPLEMENTATION NOTES
421+
The complexity of any of the BFS methods or DFS is similar.
407422

408423
*Complexity Analysis*:
409424

410-
- Time: `O(?)`. WHY?
411-
- Space: `O(?)`. WHY?
425+
- Time: `O(n)`. We visit every node, once.
426+
- Space: `O(n)`. For BFS, the worst-case space is given by the maximum *width*. That is when the binary tree is complete so that the last level would have `(n-1)/2` nodes, thus `O(n)`. For the DFS, the space complexity will be given by the tree's maximum *height*. In the worst-case, the binary tree is skewed to the right so that we will have an implicit call stack of size `n`.
412427

413428

414429
// [#linkedlist-q-FILENAME]

‎book/content/part02/array.asc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ maxSubArray([-3, 4,-1, 2, 1, -5]); // 6 (sum [4,-1, 2, 1])
293293
maxSubArray([-2, 1, -3, 4, -1, 3, 1]); // 7 (sum [4,-1, 3, 1])
294294
----
295295

296-
_Seen in interviews at: Amazon, Apple, Google, Microsoft, Facebook_
296+
// _Seen in interviews at: Amazon, Apple, Google, Microsoft, Facebook_
297297
// end::array-q-max-subarray[]
298298

299299
[source, javascript]
@@ -320,7 +320,7 @@ maxProfit([5, 10, 5, 10]) // 5 (buying at 5 and selling at 10)
320320
321321
----
322322

323-
_Seen in interviews at: Amazon, Facebook, Bloomberg_
323+
// _Seen in interviews at: Amazon, Facebook, Bloomberg_
324324
// end::array-q-buy-sell-stock[]
325325

326326
[source, javascript]

‎book/content/part02/linked-list.asc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ mergeTwoLists(2->3->4, 1->2); // 1->2->2->3->4
301301
mergeTwoLists(2->3->4,null); // 2->3->4
302302
----
303303

304-
_Seen in interviews at: Amazon, Adobe, Microsoft, Google_
304+
// _Seen in interviews at: Amazon, Adobe, Microsoft, Google_
305305
// end::linkedlist-q-merge-lists[]
306306

307307
[source, javascript]
@@ -329,7 +329,7 @@ hasSameData(hello, hel->lo); // true
329329
hasSameData(he->ll->o, h->i); // false
330330
----
331331

332-
_Seen in interviews at: Facebook_
332+
// _Seen in interviews at: Facebook_
333333
// end::linkedlist-q-linkedlist-same-data[]
334334

335335
[source, javascript]

‎book/content/part02/queue.asc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ counter.request(3100); // 1 (last requests was 100 ms ago, > 10ms, so doesn't co
103103
counter.request(3105); // 2 (last requests was 5 ms ago, <= 10ms, so it counts)
104104
----
105105

106-
_Seen in interviews at: Google, Bloomberg, Yandex_
106+
// _Seen in interviews at: Google, Bloomberg, Yandex_
107107
// end::queue-q-recent-counter[]
108108

109109

@@ -135,7 +135,7 @@ expect(snakeGame.move('L')).toEqual(2); // 2 (ate food2)
135135
expect(snakeGame.move('U')).toEqual(-1); // -1 (hit wall)
136136
----
137137

138-
_Seen in interviews at: Amazon, Bloomberg, Apple_
138+
// _Seen in interviews at: Amazon, Bloomberg, Apple_
139139
// end::queue-q-design-snake-game[]
140140

141141
[source, javascript]

‎book/content/part02/stack.asc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ isParenthesesValid('[{]}'); // false (brakets are not closed in the right order)
106106
isParenthesesValid('([{)}]'); // false (closing is out of order)
107107
----
108108

109-
_Seen in interviews at: Amazon, Bloomberg, Facebook, Citadel_
109+
// _Seen in interviews at: Amazon, Bloomberg, Facebook, Citadel_
110110
// end::stack-q-valid-parentheses[]
111111

112112
[source, javascript]
@@ -135,7 +135,7 @@ dailyTemperatures([30, 28, 50, 40, 30]); // [2 (to 50), 1 (to 28), 0, 0, 0]
135135
dailyTemperatures([73, 69, 72, 76, 73, 100]); // [3, 1, 1, 0, 1, 100]
136136
----
137137

138-
_Seen in interviews at: Amazon, Adobe, Cisco_
138+
// _Seen in interviews at: Amazon, Adobe, Cisco_
139139
// end::stack-q-daily-temperatures[]
140140

141141
[source, javascript]

‎book/content/part03/tree-intro.asc

Lines changed: 105 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ A tree is a non-linear data structure where a node can have zero or more connect
1212
.Tree Data Structure: root node and descendants.
1313
image::image31.jpg[image,width=404,height=240]
1414

15-
As you can see in the picture above, this data structure resembles an inverted tree hence the name. It starts with a *root* node and *branch* off with its descendants, and finally *leaves*.
15+
As you can see in the picture above, this data structure resembles an inverted tree, hence the name. It starts with a *root* node and *branch* off with its descendants, and finally *leaves*.
1616

1717
==== Implementing a Tree
1818

@@ -54,17 +54,17 @@ image::image31.jpg[image]
5454

5555
==== Types of Binary Trees
5656

57-
There are different kinds of trees depending on the restrictions. E.g. The trees that have two children or less are called *binary tree*, while trees with at most three children are called *Ternary Tree*. Since binary trees are most common we are going to cover them here and others in another chapter.
57+
There are different kinds of trees, depending on the restrictions. E.g. The trees with two children or less are called *binary tree*, while trees with at most three children are called *Ternary Tree*. Since binary trees are the most common, we will cover them here and others in another chapter.
5858

5959
===== Binary Tree
6060
(((Binary Tree)))
6161
(((Data Structures, Non-Linear, Binary Tree)))
62-
The binary restricts the nodes to have at most two children. Trees, in general, can have 3, 4, 23 or more, but not binary trees.
62+
The binary restricts the nodes to have at most two children. Trees can have 0, 1, 2, 7, or more, but not binary trees.
6363

6464
.Binary tree has at most 2 children while non-binary trees can have more.
6565
image::image32.png[image,width=321,height=193]
6666

67-
Binary trees are one of the most used kinds of tree, and they are used to build other data structures.
67+
Binary trees are one of the most used kinds of trees, and they are used to build other data structures.
6868

6969
.Binary Tree Applications
7070
- <<part03-graph-data-structures#map>>
@@ -90,7 +90,7 @@ image::image33.png[image,width=348,height=189]
9090
(((Max-Heap)))
9191
(((Min-Heap)))
9292
(((Data Structures, Non-Linear, Binary Heap)))
93-
The heap (max-heap) is a type of binary tree where the parent's value is higher than the value of both children. Opposed to the BST, the left child doesn’t have to be smaller than the right child.
93+
The heap (max-heap) is a type of binary tree where the parent's value is higher than both children's value. Opposed to the BST, the left child doesn’t have to be smaller than the right child.
9494

9595
.Heap vs BST
9696
image::image34.png[image,width=325,height=176]
@@ -118,72 +118,86 @@ indexterm:[Runtime, Logarithmic]
118118
// tag::binary-tree-q-diameter-of-binary-tree[]
119119
===== Binary Tree Diameter
120120

121-
*BT-1*) _Find the diameter of a binary tree. The diameter of a tree is defined as the longest possible path from two nodes (it doesn't need to include the root). The lenth of diameter is calculated by couting the number of edges on the path._
121+
*BT-1*) _Find the diameter of a binary tree. A tree's diameter is the longest possible path from two nodes (it doesn't need to include the root). The length of a diameter is calculated by counting the number of edges on the path._
122122

123123
// end::binary-tree-q-diameter-of-binary-tree[]
124124

125-
_Seen in interviews at: Facebook, Amazon, Google_
126-
127-
Example 1:
128-
129-
[graphviz, tree-diameter-example-1, png]
130-
....
131-
graph G {
132-
1 -- 2 [color=red]
133-
1 -- 3 [color=red]
134-
135-
2 -- 4
136-
2 -- 5 [color=red]
137-
}
138-
....
139-
140-
[source, javascript]
141-
----
142-
diameterOfBinaryTree(toBinaryTree([1,2,3,4,5])); // 3 (the path 3-1-2-5 or 3-1-2-4)
143-
----
144-
145-
Example 2:
146-
147-
[graphviz, tree-diameter-example-2, png]
148-
....
149-
graph G {
150-
1
151-
2
152-
3
153-
4
154-
5
155-
6
156-
"null" [color=white, fontcolor = white]
157-
"null." [color=white, fontcolor = white]
158-
7
159-
8
160-
"null.." [color=white, fontcolor = white]
161-
"null..." [color=white, fontcolor = white]
162-
9
163-
164-
1 -- 2
165-
1 -- 3
166-
167-
3 -- 4 [color=red]
168-
3 -- 5 [color=red]
169-
170-
4 -- 6 [color=red]
171-
4 -- "null." [color=white]
172-
173-
5 -- "null" [color=white]
174-
5 -- 7 [color=red]
175-
176-
6 -- 8 [color=red]
177-
6 -- "null.." [color=white]
178-
179-
7 -- "null..." [color=white]
180-
7 -- 9 [color=red]
181-
}
182-
....
125+
// _Seen in interviews at: Facebook, Amazon, Google_
126+
127+
// Example 1:
128+
// [graphviz, tree-diameter-example-1, png]
129+
// ....
130+
// graph G {
131+
// 1 -- 2 [color=red]
132+
// 1 -- 3 [color=red]
133+
134+
// 2 -- 4
135+
// 2 -- 5 [color=red]
136+
// }
137+
// ....
138+
139+
// Example 2:
140+
// [graphviz, tree-diameter-example-2, png]
141+
// ....
142+
// graph G {
143+
// 1
144+
// 2
145+
// 3
146+
// 4
147+
// 5
148+
// 6
149+
// "null" [color=white, fontcolor = white]
150+
// "null." [color=white, fontcolor = white]
151+
// 7
152+
// 8
153+
// "null.." [color=white, fontcolor = white]
154+
// "null..." [color=white, fontcolor = white]
155+
// 9
156+
157+
// 1 -- 2
158+
// 1 -- 3
159+
160+
// 3 -- 4 [color=red]
161+
// 3 -- 5 [color=red]
162+
163+
// 4 -- 6 [color=red]
164+
// 4 -- "null." [color=white]
165+
166+
// 5 -- "null" [color=white]
167+
// 5 -- 7 [color=red]
168+
169+
// 6 -- 8 [color=red]
170+
// 6 -- "null.." [color=white]
171+
172+
// 7 -- "null..." [color=white]
173+
// 7 -- 9 [color=red]
174+
// }
175+
// ....
176+
177+
Examples:
183178

184179
[source, javascript]
185180
----
186-
diameterOfBinaryTree(toBinaryTree([1,2,3,null,null,4,5,6,null,null,7,8,null,null,9])); // 6 (the path 8-6-4-3-5-7-9)
181+
/* 1
182+
/ \
183+
2 3
184+
/ \
185+
4 5 */
186+
diameterOfBinaryTree(toBinaryTree([1,2,3,4,5])); // 3
187+
// For len 3, the path could be 3-1-2-5 or 3-1-2-4
188+
189+
/* 1
190+
/ \
191+
2 3
192+
/ \
193+
4 5
194+
/ \
195+
6 7
196+
/ \
197+
8 9 */
198+
const array = [1,2,3,null,null,4,5,6,null,null,7,8,null,null,9];
199+
const tree = BinaryTreeNode.from(array);
200+
diameterOfBinaryTree(tree); // 6 (path: 8-6-4-3-5-7-9)
187201
----
188202

189203
Starter code:
@@ -198,17 +212,37 @@ _Solution: <<binary-tree-q-diameter-of-binary-tree>>_
198212

199213

200214

201-
// tag::binary-tree-q-FILENAME[]
202-
===== Recent Counter
203215

204-
*BT-2*) _._
216+
// tag::binary-tree-q-binary-tree-right-side-view[]
217+
===== Binary Tree from right side view
205218

206-
Example:
219+
*BT-2*) _Imagine that you are viewing the tree from the right side. What nodes would you see?_
220+
221+
// end::binary-tree-q-binary-tree-right-side-view[]
222+
223+
// _Seen in interviews at: Facebook, Amazon, ByteDance (TikTok)._
224+
225+
Examples:
207226

208227
[source, javascript]
209228
----
229+
/*
230+
1 <- 1 (only node on level)
231+
/ \
232+
2 3 <- 3 (3 is the rightmost)
233+
\
234+
4 <- 4 (only node on level) */
235+
rightSideView(BinaryTreeNode.from([1, 2, 3, null, 4])); // [1, 3, 4]
236+
237+
rightSideView(BinaryTreeNode.from([])); // []
238+
rightSideView(BinaryTreeNode.from([1, 2, 3, null, 5, null, 4, 6])); // [1, 3, 4, 6]
239+
----
210240

241+
Starter code:
242+
243+
[source, javascript]
244+
----
245+
include::../../interview-questions/binary-tree-right-side-view.js[tags=description;placeholder]
211246
----
212247

213-
_Seen in interviews at: X_
214-
// end::binary-tree-q-FILENAME[]
248+
_Solution: <<binary-tree-q-binary-tree-right-side-view>>_
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const { Queue } = require('../../src/index');
2+
3+
// tag::description[]
4+
/**
5+
* Find the rightmost nodes by level.
6+
*
7+
* @example
8+
* 1 <- 1
9+
* / \
10+
* 2 3 <- 3
11+
* \
12+
* 4 <- 4
13+
*
14+
* @param {BinaryTreeNode} root - The root of the binary tree.
15+
* @returns {number[]} - array with the rightmost nodes values.
16+
*/
17+
function rightSideView(root) {
18+
// end::description[]
19+
// tag::placeholder[]
20+
// write your code here...
21+
// end::placeholder[]
22+
// tag::solution[]
23+
if (!root) return [];
24+
const queue = new Queue([root]);
25+
const ans = [];
26+
27+
while (queue.size) {
28+
const { size } = queue;
29+
for (let i = 0; i < size; i++) {
30+
const node = queue.dequeue();
31+
if (i === size - 1) ans.push(node.value);
32+
if (node.left) queue.enqueue(node.left);
33+
if (node.right) queue.enqueue(node.right);
34+
}
35+
}
36+
37+
return ans;
38+
// end::solution[]
39+
// tag::description[]
40+
}
41+
// end::description[]
42+
43+
// tag::dfs[]
44+
function rightSideViewDfs(root) {
45+
const ans = [];
46+
47+
const dfs = (node, level = 0) => {
48+
if (!node) return;
49+
if (level === ans.length) ans.push(node.value);
50+
dfs(node.right, level + 1); // right side first!
51+
dfs(node.left, level + 1);
52+
};
53+
54+
dfs(root);
55+
return ans;
56+
}
57+
// end::dfs[]
58+
59+
module.exports = { rightSideView, rightSideViewDfs };
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const { rightSideView, rightSideViewDfs } = require('./binary-tree-right-side-view');
2+
const { BinaryTreeNode } = require('../../src/index');
3+
4+
[rightSideView, rightSideViewDfs].forEach((fn) => {
5+
describe(`Binary Tree: ${fn.name}`, () => {
6+
it('should work with null', () => {
7+
const actual = null;
8+
const expected = [];
9+
expect(fn(actual)).toEqual(expected);
10+
});
11+
12+
it('should work with small case', () => {
13+
const actual = BinaryTreeNode.from([1, 2, 3, null, 4]);
14+
const expected = [1, 3, 4];
15+
expect(fn(actual)).toEqual(expected);
16+
});
17+
18+
it('should work with other case', () => {
19+
const actual = BinaryTreeNode.from([1, 2, 3, null, 5, null, 4, 6]);
20+
const expected = [1, 3, 4, 6];
21+
expect(fn(actual)).toEqual(expected);
22+
});
23+
});
24+
});

‎book/interview-questions/diameter-of-binary-tree.spec.js

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,13 @@ const { diameterOfBinaryTree } = require('./diameter-of-binary-tree');
22
const { BinaryTreeNode } = require('../../src/index');
33

44
describe('Binary Tree: Diameter', () => {
5-
function toBinaryTree(array, index = 0) {
6-
if (index >= array.length) return null;
7-
const node = new BinaryTreeNode(array[index]);
8-
node.left = toBinaryTree(array, index * 2 + 1);
9-
node.right = toBinaryTree(array, index * 2 + 2);
10-
return node;
11-
}
12-
135
it('should find the diameter', () => {
14-
expect(diameterOfBinaryTree(toBinaryTree([1, 2, 3, 4, 5]))).toEqual(3);
6+
const actual = BinaryTreeNode.from([1, 2, 3, 4, 5]);
7+
expect(diameterOfBinaryTree(actual)).toEqual(3);
158
});
169

1710
it('should find the diameter when does not pass through the root node', () => {
18-
const tree = [1, 2, 3, null, null, 4, 5, 6, null, null, 7, 8, null, null, 9];
19-
expect(diameterOfBinaryTree(toBinaryTree(tree))).toEqual(6);
11+
const arr = [1, 2, 3, null, null, 4, 5, 6, null, null, 7, 8, null, null, 9];
12+
expect(diameterOfBinaryTree(BinaryTreeNode.from(arr))).toEqual(6);
2013
});
2114
});

0 commit comments

Comments
 (0)
Please sign in to comment.