-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: searching in tree structure for DOM Node
- Loading branch information
1 parent
7aa8fa8
commit 7311e80
Showing
1 changed file
with
170 additions
and
0 deletions.
There are no files selected for viewing
170 changes: 170 additions & 0 deletions
170
19.find-corresponding-node-in-two-identical-DOM-tree.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
# 19. find corresponding node in two identical DOM tree | ||
|
||
### Problem | ||
|
||
https://bigfrontend.dev/problem/find-corresponding-node-in-two-identical-DOM-tree | ||
|
||
# | ||
|
||
### Problem Description | ||
|
||
Given two same DOM tree **A**, **B**, and an Element **a** in **A**, find the corresponding Element **b** in **B**. | ||
|
||
By **corresponding**, we mean **a** and **b** have the same relative position to their DOM tree root. | ||
|
||
_follow up_ | ||
|
||
This could a problem on general Tree structure with only `children`. | ||
|
||
Could you solve it recursively and iteratively? | ||
|
||
Could you solve this problem both with special DOM api for better performance? | ||
|
||
What are the time cost for each solution? | ||
|
||
# | ||
|
||
### Recursive Solution | ||
|
||
```js | ||
/** | ||
* @param {HTMLElement} rootA | ||
* @param {HTMLElement} rootB - rootA and rootB are clone of each other | ||
* @param {HTMLElement} nodeA | ||
*/ | ||
const findCorrespondingNode = (rootA, rootB, targetNode) => { | ||
if (rootA === targetNode) { | ||
return rootB; | ||
} | ||
|
||
for (let i = 0; i < rootA.childNodes.length; i++) { | ||
const result = findCorrespondingNode( | ||
rootA.childNodes[i], | ||
rootB.childNodes[i], | ||
targetNode | ||
); | ||
if (result) { | ||
return result; | ||
} | ||
} | ||
return null; | ||
}; | ||
``` | ||
|
||
# | ||
|
||
### Iterative Solution | ||
|
||
```js | ||
/** | ||
* @param {HTMLElement} rootA | ||
* @param {HTMLElement} rootB - rootA and rootB are clone of each other | ||
* @param {HTMLElement} nodeA | ||
*/ | ||
const findCorrespondingNode = (rootA, rootB, target) => { | ||
let stackA = [rootA]; | ||
let stackB = [rootB]; | ||
|
||
while (stackA.length > 0) { | ||
const nodeA = stackA.pop(); | ||
const nodeB = stackB.pop(); | ||
|
||
if (nodeA === target) { | ||
return nodeB; | ||
} | ||
|
||
stackA.push(...nodeA.childNodes); | ||
stackB.push(...nodeB.childNodes); | ||
} | ||
|
||
return; | ||
}; | ||
``` | ||
|
||
# | ||
|
||
### Solution with DOM API `parentNode` | ||
|
||
```js | ||
/** | ||
* @param {HTMLElement} rootA | ||
* @param {HTMLElement} rootB - rootA and rootB are clone of each other | ||
* @param {HTMLElement} nodeA | ||
*/ | ||
const findCorrespondingNode = (rootA, rootB, target) => { | ||
const path = []; | ||
let node = target; | ||
while (node !== rootA) { | ||
const parentNode = node.parentNode; | ||
const childNodes = Array.from(parentNode.childNodes); | ||
path.push(childNodes.indexOf(node)); | ||
node = parentNode; | ||
} | ||
|
||
return path.reduceRight((node, index) => node.childNodes[index], rootB); | ||
}; | ||
``` | ||
# | ||
|
||
### Usage | ||
|
||
```js | ||
const rootA = { | ||
value: "A", | ||
childNodes: [ | ||
{ | ||
value: "B", | ||
childNodes: [ | ||
{ | ||
value: "C", | ||
childNodes: [], | ||
}, | ||
], | ||
}, | ||
], | ||
}; | ||
|
||
const rootB = { | ||
value: "A", | ||
childNodes: [ | ||
{ | ||
value: "B", | ||
childNodes: [ | ||
{ | ||
value: "C", | ||
childNodes: [], | ||
}, | ||
], | ||
}, | ||
], | ||
}; | ||
|
||
const targetNode = rootA.childNodes[0].childNodes[0]; | ||
|
||
console.log(findCorrespondingNode(rootA, rootB, targetNode)); | ||
``` | ||
|
||
|
||
# | ||
|
||
### Explanation | ||
|
||
Let's break down the `findCorrespondingNode` function and its usage: | ||
|
||
1. **Function Definition**: The `findCorrespondingNode` function takes three arguments: `nodeA`, `nodeB`, and `targetNode`. It's designed to find a node in `nodeB` that corresponds to `targetNode` in `nodeA`, assuming `nodeA` and `nodeB` have the same structure. | ||
|
||
2. **Base Cases**: If `nodeA` is the `targetNode`, it returns `nodeB`. If `nodeA` has no child nodes, it returns `null` because there's no corresponding node in `nodeB`. | ||
|
||
3. **Recursion**: If `nodeA` is not the `targetNode` and it has child nodes, it recursively calls `findCorrespondingNode` for each pair of child nodes in `nodeA` and `nodeB`. If a recursive call returns a non-null result, it returns that result. | ||
|
||
4. **Usage**: The usage example creates two identical tree structures `nodeA` and `nodeB`, each with a root node and a single child node that also has a single child node. It then calls `findCorrespondingNode` with `nodeA`, `nodeB`, and the first child node of `nodeA` as the `targetNode`. | ||
|
||
5. **Output**: The output of the function call is the first child node of `nodeB`, which corresponds to the `targetNode` in `nodeA`. This is logged to the console. | ||
|
||
This `findCorrespondingNode` function can be used to find corresponding nodes in two tree structures that have the same structure. It uses a depth-first search strategy, checking the current node before checking the child nodes. | ||
|
||
# | ||
|
||
### Reference | ||
|
||
[Problem Discuss](https://bigfrontend.dev/problem/find-corresponding-node-in-two-identical-DOM-tree/discuss) |