-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtree.js
354 lines (310 loc) · 8.31 KB
/
tree.js
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
// tags: #tree #data-structure
class Node {
constructor(val) {
this.val = val;
this.left = this.right = null;
}
}
// BFS思路建完全二叉树
function create(list) {
const arr = [...list];
const queue = [];
const root = new Node(arr.shift());
queue.push(root);
while (arr.length) {
const node = queue.shift();
node.left = new Node(arr.shift());
queue.push(node.left);
if (arr.length) {
node.right = new Node(arr.shift());
queue.push(node.right);
}
}
return root;
}
// DFS思路 前中序、后中序可还原树,前后序信息不足
// 前中序还原二叉树
function restoreTree(preorder, inorder) {
if (!preorder || preorder.length < 1) {
return null;
}
const root = preorder[0];
const treeNode = new Node(root);
if (preorder.length === 1) {
return treeNode;
}
let rootIndex = inorder.indexOf(root);
let inLeftTree = inorder.slice(0, rootIndex);
let preLeftTree = preorder.slice(1, inLeftTree.length + 1);
let inRightTree = inorder.slice(rootIndex + 1, inorder.length);
let preRightTree = preorder.slice(preLeftTree.length + 1, preorder.length);
treeNode.left = restoreTree(preLeftTree, inLeftTree);
treeNode.right = restoreTree(preRightTree, inRightTree);
return treeNode;
}
// BFS、DFS 本质区别是使用队列还是栈, 递归只是用了系统的栈
function bfs(root) {
if (!root) return;
const visit = [];
const queue = [];
queue.push(root);
while (queue.length) {
const node = queue.shift();
visit.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
return visit;
}
function dfsPreOrder(root) {
const visit = [];
function traverse(node) {
if (!node) return;
// 在刚进入节点时处理逻辑,这里的逻辑是简单的插入数组
visit.push(node.val);
traverse(node.left);
traverse(node.right);
}
traverse(root);
return visit;
}
function dfsInOrder(root) {
const visit = [];
function traverse(node) {
if (!node) return;
traverse(node.left);
// 在左子树遍历完,要开始遍历右子树时处理逻辑
visit.push(node.val);
traverse(node.right);
}
traverse(root);
return visit;
}
function dfsPostOrder(root) {
const visit = [];
function traverse(node) {
if (!node) return;
traverse(node.left);
traverse(node.right);
// 在将要离开节点时处理逻辑
visit.push(node.val);
}
traverse(root);
return visit;
}
function dfsPreOrderLoop(root) {
const visit = [];
const stack = [];
if (root) stack.push(root);
while (stack.length) {
const node = stack.pop();
visit.push(node.val);
if (node.right) stack.push(node.right);
if (node.left) stack.push(node.left);
}
return visit;
}
function dfsInOrderLoop(root) {
const visit = [];
const stack = [];
while (true) {
while (root) {
stack.push(root);
root = root.left;
}
if (!stack.length) break;
root = stack.pop();
visit.push(root.val);
root = root.right;
}
return visit;
}
function dfsPostOrderLoop(root) {
const visit = [];
const stack = [];
if (root) stack.push(root);
while (stack.length) {
const node = stack.pop();
visit.push(node.val);
if (node.left) stack.push(node.left);
if (node.right) stack.push(node.right);
}
return visit.reverse();
}
function depth(root) {
if (!root) return 0;
const l = depth(root.left);
const r = depth(root.right);
return Math.max(l, r) + 1;
}
function width(root) {
if (!root) return null;
const widths = [];
const queue = [];
queue.push(root);
while (queue.length) {
const w = queue.length;
widths.push(w);
for (let i = 0; i < w; i++) {
const node = queue.shift();
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
}
return widths;
}
// 镜面反转二叉树
function rotate(root) {
if (!root) return;
[root.left, root.right] = [root.right, root.left];
rotate(root.left);
rotate(root.right);
}
// 通过构建反向连接,获取根结点到任意节点的路径
// 获得任意两条路径, 路径头部节点序列去重后连接上就是两节点的最短路, 头部开始最后一个相同节点就是两条连接的最近公共父亲
function routeByLink(root, end) {
const path = [];
function linkParent(node) {
if (!node) return;
if (node.left) {
node.left.parent = node;
linkParent(node.left);
}
if (node.right) {
node.right.parent = node;
linkParent(node.right);
}
}
linkParent(root);
while (end) {
path.unshift(end);
end = end.parent;
}
return path;
}
// 可优化为 routeByDFS 版本
function routeByStack(root, end) {
const path = [];
let found = false;
function travel(root) {
if (!root) return;
path.push(root);
if (root === end) found = true;
if (!found) travel(root.left);
if (!found) travel(root.right);
if (!found) path.pop(root);
}
travel(root);
return path;
}
// 获取根结点到任意节点的路径,找到终点后开始收敛
function routeByDFS(root, end) {
return travel(root);
function travel(node, path = []) {
if (!node) return;
// 前中后序皆可, 节点满足条件时才执行逻辑
if (node === end) {
// 基础深搜变形版本, 大部分算法都是基础版本变形
// 深搜查找元素 找到后执行 return 停止本层后续查找, 同时 return 带出信息给上层使用
path.unshift(node);
return path;
}
// 收到 travel 内部 return 信息后停止后续递归逻辑, 一直传递到顶层, 若无信息 travel 拿到 undefined
if (travel(node.left, path)) {
path.unshift(node);
return path;
}
if (travel(node.right, path)) {
path.unshift(node);
return path;
}
}
}
function lowestCommonAncestor(root, p, q) {
// 当前信息足够可以返回结束
if (root === null || root === p || root === q) {
return root;
}
// 信息不足, 分别从左右子树收集信息
let left = lowestCommonAncestor(root.left, p, q);
let right = lowestCommonAncestor(root.right, p, q);
// 收集完信息后处理和返回
if (left && right) {
return root;
}
return left || right;
}
// 最低的一层,叶子节点不一定是
function isLastLayer(root, node) {
return depth(root) === routeByDFS(root, node).length;
}
// 按满二叉树宽搜打印树内容,空节点补null
function bfsPrint(root) {
let array = [];
let queue = [];
queue.push(root);
while (queue.length) {
const current = queue.shift();
array.push(current.val);
if (current.left) {
queue.push(current.left);
} else if (!isLastLayer(root, current)) {
current.left = new Node();
queue.push(current.left);
}
if (current.right) {
queue.push(current.right);
} else if (!isLastLayer(root, current)) {
current.right = new Node();
queue.push(current.right);
}
}
return array;
}
/* test code */
const assert = require("node:assert/strict");
const NODECOUNT = 20;
const listGenetator = (number) =>
[...Array(number)].map(() => Math.floor(Math.random() * 100));
const list = listGenetator(NODECOUNT);
const tree = create(list);
// 层数
const treeDepth = Math.ceil(Math.log(NODECOUNT + 1) / Math.log(2));
// 每层宽度
const treeWidthList = ((nodeCount) => {
const list = [];
while (nodeCount > 0) {
const width = 2 ** list.length;
if (nodeCount >= width) {
nodeCount -= width;
list.push(width);
} else {
list.push(nodeCount);
break;
}
}
return list;
})(NODECOUNT);
// 生成随机叶子节点
const randomLeaf = ((root) => {
while (true) {
const direction = Math.random() > 0.5 ? "left" : "right";
if (root[direction]) root = root[direction];
else break;
}
return root;
})(tree);
assert.deepEqual(bfs(tree), list);
assert.deepEqual(dfsPreOrderLoop(tree), dfsPreOrder(tree));
assert.deepEqual(dfsInOrderLoop(tree), dfsInOrder(tree));
assert.deepEqual(dfsPostOrderLoop(tree), dfsPostOrder(tree));
assert.equal(depth(tree), treeDepth);
assert.deepEqual(width(tree), treeWidthList);
assert.deepEqual(
routeByLink(tree, randomLeaf).map((i) => i.val),
routeByStack(tree, randomLeaf).map((i) => i.val)
);
assert.deepEqual(
routeByLink(tree, randomLeaf).map((i) => i.val),
routeByDFS(tree, randomLeaf).map((i) => i.val)
);