|
1 | 1 | /**
|
2 |
| - * @typedef {Object} Callbacks |
3 |
| - * @property {function(node: BinaryTreeNode, child: BinaryTreeNode): boolean} allowTraversal - |
4 |
| - * Determines whether DFS should traverse from the node to its child. |
| 2 | + * @typedef {Object} TraversalCallbacks |
| 3 | + * |
| 4 | + * @property {function(node: BinaryTreeNode, child: BinaryTreeNode): boolean} allowTraversal |
| 5 | + * - Determines whether DFS should traverse from the node to its child. |
| 6 | + * |
5 | 7 | * @property {function(node: BinaryTreeNode)} enterNode - Called when DFS enters the node.
|
| 8 | + * |
6 | 9 | * @property {function(node: BinaryTreeNode)} leaveNode - Called when DFS leaves the node.
|
7 | 10 | */
|
8 | 11 |
|
9 | 12 | /**
|
10 |
| - * @param {Callbacks} [callbacks] |
11 |
| - * @returns {Callbacks} |
| 13 | + * Extend missing traversal callbacks with default callbacks. |
| 14 | + * |
| 15 | + * @param {TraversalCallbacks} [callbacks] - The object that contains traversal callbacks. |
| 16 | + * @returns {TraversalCallbacks} - Traversal callbacks extended with defaults callbacks. |
12 | 17 | */
|
13 | 18 | function initCallbacks(callbacks = {}) {
|
14 |
| - const initiatedCallback = callbacks; |
| 19 | + // Init empty callbacks object. |
| 20 | + const initiatedCallbacks = {}; |
15 | 21 |
|
| 22 | + // Empty callback that we will use in case if user didn't provide real callback function. |
16 | 23 | const stubCallback = () => {};
|
17 |
| - const defaultAllowTraversal = () => true; |
| 24 | + // By default we will allow traversal of every node |
| 25 | + // in case if user didn't provide a callback for that. |
| 26 | + const defaultAllowTraversalCallback = () => true; |
18 | 27 |
|
19 |
| - initiatedCallback.allowTraversal = callbacks.allowTraversal || defaultAllowTraversal; |
20 |
| - initiatedCallback.enterNode = callbacks.enterNode || stubCallback; |
21 |
| - initiatedCallback.leaveNode = callbacks.leaveNode || stubCallback; |
| 28 | + // Copy original callbacks to our initiatedCallbacks object or use default callbacks instead. |
| 29 | + initiatedCallbacks.allowTraversal = callbacks.allowTraversal || defaultAllowTraversalCallback; |
| 30 | + initiatedCallbacks.enterNode = callbacks.enterNode || stubCallback; |
| 31 | + initiatedCallbacks.leaveNode = callbacks.leaveNode || stubCallback; |
22 | 32 |
|
23 |
| - return initiatedCallback; |
| 33 | + // Returned processed list of callbacks. |
| 34 | + return initiatedCallbacks; |
24 | 35 | }
|
25 | 36 |
|
26 | 37 | /**
|
27 |
| - * @param {BinaryTreeNode} node |
28 |
| - * @param {Callbacks} callbacks |
| 38 | + * Recursive depth-first-search traversal for binary. |
| 39 | + * |
| 40 | + * @param {BinaryTreeNode} node - binary tree node that we will start traversal from. |
| 41 | + * @param {TraversalCallbacks} callbacks - the object that contains traversal callbacks. |
29 | 42 | */
|
30 | 43 | export function depthFirstSearchRecursive(node, callbacks) {
|
| 44 | + // Call the "enterNode" callback to notify that the node is going to be entered. |
31 | 45 | callbacks.enterNode(node);
|
32 | 46 |
|
33 |
| - // Traverse left branch. |
| 47 | + // Traverse left branch only if case if traversal of the left node is allowed. |
34 | 48 | if (node.left && callbacks.allowTraversal(node, node.left)) {
|
35 | 49 | depthFirstSearchRecursive(node.left, callbacks);
|
36 | 50 | }
|
37 | 51 |
|
38 |
| - // Traverse right branch. |
| 52 | + // Traverse right branch only if case if traversal of the right node is allowed. |
39 | 53 | if (node.right && callbacks.allowTraversal(node, node.right)) {
|
40 | 54 | depthFirstSearchRecursive(node.right, callbacks);
|
41 | 55 | }
|
42 | 56 |
|
| 57 | + // Call the "leaveNode" callback to notify that traversal |
| 58 | + // of the current node and its children is finished. |
43 | 59 | callbacks.leaveNode(node);
|
44 | 60 | }
|
45 | 61 |
|
46 | 62 | /**
|
47 |
| - * @param {BinaryTreeNode} rootNode |
48 |
| - * @param {Callbacks} [callbacks] |
| 63 | + * Perform depth-first-search traversal of the rootNode. |
| 64 | + * For every traversal step call "allowTraversal", "enterNode" and "leaveNode" callbacks. |
| 65 | + * See TraversalCallbacks type definition for more details about the shape of callbacks object. |
| 66 | + * |
| 67 | + * @param {BinaryTreeNode} rootNode - The node from which we start traversing. |
| 68 | + * @param {TraversalCallbacks} [callbacks] - Traversal callbacks. |
49 | 69 | */
|
50 | 70 | export default function depthFirstSearch(rootNode, callbacks) {
|
51 |
| - depthFirstSearchRecursive(rootNode, initCallbacks(callbacks)); |
| 71 | + // In case if user didn't provide some callback we need to replace them with default ones. |
| 72 | + const processedCallbacks = initCallbacks(callbacks); |
| 73 | + |
| 74 | + // Now, when we have all necessary callbacks we may proceed to recursive traversal. |
| 75 | + depthFirstSearchRecursive(rootNode, processedCallbacks); |
52 | 76 | }
|
0 commit comments