Skip to content

Commit 83fd294

Browse files
committed
V1.4.3 Add Async Steps for BST remove and AVL rotation.
1 parent 855a8c9 commit 83fd294

File tree

12 files changed

+138
-43
lines changed

12 files changed

+138
-43
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,9 @@ No compatibility testing. Functions are only guaranteed in Chrome.
3737

3838
如果你想帮助我提升前端兼容性, 欢迎随时联系我!
3939

40-
别忘了给Star哟
40+
别忘了给Star哟
41+
42+
## Interesting Samples
43+
44+
#### AVL
45+
21,12,28,7,17,25,31,4,10,15,18,23,27,30,32,2,6,9,11,14,16,17.5,19,22,24,26,,29,,,,1,3,5,,8,,,,13,14.5,15.5,,17.3,,,,21.5,,,,,,,,0,,,,,,,,12.5

dist/TreePlayground.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
</head>
1212

1313
<body>
14-
<div id="header" style="position: fixed; left: 0; top: 0;">
15-
<p style="color: gray">请使用chrome浏览器</p>
14+
<div id="header" style="position: fixed; left: 5px; top: 5px;">
15+
<p style="color: gray">Recommend Chrome on PC</p>
1616
</div>
1717
<div id="TreePlayground" @mousemove='onTPMouseMove($event)' @touchmove='onTPMouseMove($event)'>
1818
<!-- Top Toolbar -->
@@ -71,7 +71,7 @@ <h4>Scale: <label v-text="commonParams.treeScale + '%'"></h4>
7171
:style="{'left': e[0]+'px', 'top': e[1]+'px', 'width': e[2]+'px', 'height': e[3]+'px'}"></div>
7272
</div>
7373
</div>
74-
<div id="footer" style="position: fixed; left: 0; bottom: 0;">Copyright @ 2020 NitroMelon
74+
<div id="footer" style="position: fixed; left: 5px; bottom: 5px;">Copyright @ 2020 NitroMelon
7575
<a href="https://github.com/hwc0919/TreePlayground" target="_blank">Source Code</a>
7676
<a href="https://github.com/hwc0919/TreePlayground/issues" target="_blank">Bug report</a>
7777
喜欢的话<a href="https://github.com/hwc0919/TreePlayground/stargazers" target="_blank">&nearhk;给个赞</a>

dist/bundle.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
"url": "git+https://github.com/hwc0919/TreePlayground.git"
1313
},
1414
"author": "nitromelon",
15-
"license": "ISC",
15+
"license": "MIT",
1616
"bugs": {
1717
"url": "https://github.com/hwc0919/TreePlayground/issues"
1818
},
19-
"homepage": "https://github.com/hwc0919/TreePlayground#readme",
19+
"homepage": "https://github.com/hwc0919/TreePlayground",
2020
"devDependencies": {
2121
"css-loader": "^3.4.2",
2222
"style-loader": "^1.1.3",

src/app.js

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import Vue from "../node_modules/vue/dist/vue.min"
22
import "./components/components"
3+
import { BinNode } from "./js/BinNode"
4+
import { BinTree } from "./js/BinTree"
5+
import { BST } from "./js/BST"
6+
import { AVL } from "./js/AVL"
7+
import { Splay } from "./js/Splay"
38

49
var vm = new Vue({
510
el: "#TreePlayground",
@@ -23,7 +28,7 @@ var vm = new Vue({
2328
},
2429
locks: { // TODO : seperate trvlLock and searchLock. this can wait.
2530
trvlLock: false,
26-
splayLock: false
31+
rotateLock: false
2732
},
2833
topSequence: [],
2934
BSTParams: {
@@ -213,29 +218,29 @@ var vm = new Vue({
213218
// Remove one node
214219
onRemoveOne(node) {
215220
if (this.isAnyLocked()) return false;
216-
this.messages.left = `Remove ${node.data}`;
221+
this.showMessage(`Remove ${node.data}`);
217222
if ("Splay" === this.curTreeType) { // Exception : Deal with Splay
218223
this.alertAsync(`Step 1: Splay ${node.data}`, -1);
219224
node.active = true;
220225
setTimeout(() => {
221-
this.locks.splayLock = true;
226+
this.locks.rotateLock = true;
222227
this.splayAsync(node, (rootOrNull) => {
223228
if (rootOrNull === undefined) return false;
224229
if (rootOrNull === null) throw "Error in RemoveOne";
225230
let v = rootOrNull;
226231
let tree = this.tree;
227232
tree._size--;
228-
if (!v.rc || !v.rc) {
233+
if (!v.rc || !v.rc) { // Splay Simple Situation
229234
if (!v.rc) { if (tree._root = v.lc) tree._root.parent = null; }
230235
else { if (tree._root = v.rc) tree._root.parent = null; }
231236
this.alertAsync(`Final: remove ${node.data}`, 2500);
232237
this.update();
233-
} else {
238+
} else { // Splay Complex Situation
234239
node.active = false; node.deprecated = true;
235240
this.locks.trvlLock = true;
236241
this.alertAsync(`Step 2: Elevate Succ of ${node.data}`, -1);
237242
this.searchAsync(v.rc, v.data, (_, hot) => {
238-
this.locks.splayLock = true;
243+
this.locks.rotateLock = true;
239244
this.splayAsync(hot, (newRoot) => {
240245
this.alertAsync(`Step 3: Finally remove ${node.data}`, 2500);
241246
tree.reAttachAsLC(newRoot, v.lc);
@@ -246,13 +251,78 @@ var vm = new Vue({
246251
})
247252
}, this.commonParams.interval);
248253
} else { // Deal with other trees
249-
this.tree.removeAt(node);
250-
this.tree._size--;
251-
if ("AVL" === this.curTreeType) // BugFixed0305 : _hot already at position after removeAt
252-
this.tree.solveRemoveUnbalance();
253-
this.update();
254+
if (!node.lc || !node.rc) { // Other Trees: Simple Situation
255+
this.tree.removeAt(node); this.tree._size--;
256+
this.alertAsync(`${node.data} Removed.`, 2500);
257+
this.update();
258+
if ("AVL" === this.curTreeType) {
259+
this.alertAsync(`${node.data} Removed, solve AVL Unbalance`, -1);
260+
setTimeout(() => {
261+
this.locks.rotateLock = true;
262+
this.avlRmRotateAsync(this.tree._hot, () => {
263+
this.alertAsync(`AVL Balanced again.`);
264+
this.update();
265+
});
266+
}, this.commonParams.interval);
267+
}
268+
} else { // Other Trees: Complex situation
269+
// RM Step 1: Find Succ
270+
this.alertAsync(`Step 1: Find Succ`, -1);
271+
let succ = node.succ();
272+
node.deprecated = true;
273+
this.locks.trvlLock = true; // TODO : change to srchLock
274+
this.searchAsync(node, succ.data, () => { // assert res === true
275+
this.alertAsync(`Step 2: Swap with Succ`, -1);
276+
this.update();
277+
node.deprecated = true; succ.active = true;
278+
setTimeout(() => {
279+
// RM Step 2: Swap
280+
let t = node.data; node.data = succ.data; succ.data = t;
281+
node.deprecated = false; succ.active = false;
282+
node.active = true; succ.deprecated = true;
283+
// RM Step 3: Remove
284+
this.alertAsync(`Step 3: Remove ${t}`, 2500);
285+
setTimeout(() => {
286+
this.tree.removeAt(succ);
287+
this.update();
288+
if ("AVL" === this.curTreeType) {
289+
this.alertAsync(`Step 4: Solve AVL Unbalance`, -1);
290+
setTimeout(() => {
291+
this.locks.rotateLock = true;
292+
this.avlRmRotateAsync(this.tree._hot, () => {
293+
this.alertAsync(`AVL Balanced again.`);
294+
this.update();
295+
});
296+
}, this.commonParams.interval);
297+
}
298+
}, this.commonParams.interval);
299+
}, this.commonParams.interval);
300+
})
301+
}
254302
}
255303
},
304+
// Async version of AVL.solveRemoveUnbalance
305+
avlRmRotateAsync(node, callback) { // Important: SET rotateLock BEFORE START
306+
if (!node || !this.locks.rotateLock || "AVL" !== this.curTreeType) {
307+
this.locks.rotateLock = false;
308+
if (typeof callback == "function") callback();
309+
return;
310+
}
311+
node.active = true;
312+
setTimeout(() => {
313+
let interval = this.commonParams.interval;
314+
if (!AVL.avlBalanced(node))
315+
this.tree.rotateAt(BinNode.tallerChild(BinNode.tallerChild(node)));
316+
else interval = 0;
317+
this.tree.update_height(node);
318+
this.update();
319+
node.active = true;
320+
setTimeout(() => {
321+
node.active = false;
322+
this.avlRmRotateAsync(node.parent, callback);
323+
}, interval);
324+
}, this.commonParams.interval)
325+
},
256326
// Proper Rebuild
257327
onTopBuild(sequence) {
258328
if (this.curTreeType !== "BinTree")
@@ -289,10 +359,10 @@ var vm = new Vue({
289359
this.alertAsync(nodeOrHot ? `Step 2: Splay at ${nodeOrHot.data}` : "", -1);
290360
// Wait & Splay & Insert in callback
291361
setTimeout(() => {
292-
this.locks.splayLock = true;
362+
this.locks.rotateLock = true;
293363
this.splayAsync(nodeOrHot, (rootOrNull) => {
294364
if (!res) {
295-
if (rootOrNull === undefined) return false; // `splayLock` has been reset.
365+
if (rootOrNull === undefined) return false; // `rotateLock` has been reset.
296366
this.alertAsync(`Final: ${num} Inserted`, 2500);
297367
if (rootOrNull === null) recentNode = this.tree.insertAsRoot(num);
298368
else recentNode = this.tree.insertSplitRoot(num); // Splay ONLY!!!
@@ -343,14 +413,14 @@ var vm = new Vue({
343413
if (this.curTreeType === "Splay") { // Exception & Important : Splay
344414
this.alertAsync(nodeOrHot ? `Splay at ${nodeOrHot.data}` : "", 2000);
345415
setTimeout(() => {
346-
this.locks.splayLock = true;
416+
this.locks.rotateLock = true;
347417
this.splayAsync(nodeOrHot);
348418
}, this.commonParams.interval);
349419
}
350420
});
351421
},
352-
// Search Async & Recur. Callback: (true, target) if found else (false, _hot)
353-
searchAsync(node, num, callback) {
422+
// Search Async & Recur. Callback: (true, target) if found else (false, _hot)
423+
searchAsync(node, num, callback) { // Important: SET LOCK BEFORE START!
354424
if (!this.locks.trvlLock || !node) {
355425
this.locks.trvlLock = false;
356426
if (typeof callback === "function") callback(false, this.tree._hot);
@@ -374,13 +444,13 @@ var vm = new Vue({
374444
}
375445
},
376446
// Splay Async & Recur. Callback: (null) if !v, (undefined) if locked, (_root) if success
377-
splayAsync(v, callback) {
447+
splayAsync(v, callback) { // Important: SET `rotateLock` BEFORE START!
378448
if (!v) {
379-
this.locks.splayLock = false;
449+
this.locks.rotateLock = false;
380450
if (typeof callback === "function") callback(null);
381451
return false;
382452
}
383-
if (!this.locks.splayLock) {
453+
if (!this.locks.rotateLock) {
384454
if (typeof callback === "function") callback(undefined);
385455
return false;
386456
}
@@ -395,7 +465,7 @@ var vm = new Vue({
395465
this.tree._root = v;
396466
this.update();
397467
v.active = true;
398-
this.locks.splayLock = false;
468+
this.locks.rotateLock = false;
399469
setTimeout(() => {
400470
if (typeof callback === "function") callback(v);
401471
}, this.commonParams.interval);

src/js/AVL.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { BST } from "./BST";
22
import { BinNode } from "./BinNode";
3-
class AVL extends BST {
3+
export class AVL extends BST {
44
static avlBalanced(x) {
55
let balFac = BinNode.stature(x.lc) - BinNode.stature(x.rc);
66
return -2 < balFac && balFac < 2;

src/js/BinTree.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ export class BinTree {
9090
extrNodes.push({ x: 0, y: 0, isRoot: true });
9191
return structInfo;
9292
}
93+
// 常数设置:
94+
let spacingX = 80;
95+
if (this._size > 20)
96+
spacingX = 70;
97+
else if (this._size > 40)
98+
spacingX = 65;
99+
let spacingY = 80;
100+
if (this._root.height > 10)
101+
spacingY = 70;
102+
else if (this._root.height > 20)
103+
spacingY = 60;
93104
// 逐层遍历
94105
this._root.y = 0;
95106
this._root.active = false;
@@ -100,15 +111,15 @@ export class BinTree {
100111
levels.push([]);
101112
for (let j = 0; j < levels[i].length; j++) {
102113
let node = levels[i][j];
103-
let levelY = 80 * (i + 1);
114+
let levelY = spacingY * (i + 1);
104115
// 为外部节点添加一个外部节点孩子
105116
if (node.lc === undefined) {
106117
levels[i + 1].push({ x: node.x, y: levelY, parent: node });
107118
continue;
108119
}
109120
node.active = false;
110121
node.visited = false;
111-
let deltaX = (node.data.toString().length - 1) * 6;
122+
let deltaX = Math.max(0, node.data.toString().length - 2) * 6;
112123
// 为内部节点添加两个孩子
113124
if (node.lc) {
114125
node.lc.x = node.x - deltaX;
@@ -141,7 +152,7 @@ export class BinTree {
141152
for (let j = 1; j < lastLevel.length; j++) {
142153
deltaL = deltaR;
143154
deltaR = j < lastLevel.length - 1 ? lastLevel[j + 1].x - lastLevel[j].x : 0;
144-
lastLevel[j].x = lastLevel[j - 1].x + 80;
155+
lastLevel[j].x = lastLevel[j - 1].x + spacingX;
145156
// if (lastLevel[j - 1].parent == lastLevel[j].parent) { lastLevel[j].x += deltaL }
146157
if (deltaL > 0) {
147158
lastLevel[j].x += deltaL;
@@ -178,12 +189,12 @@ export class BinTree {
178189
// 仅当父亲是内部节点时添加边
179190
let jParent = curLevel[j].parent;
180191
if (j < curLevel.length - 1 && jParent == curLevel[j + 1].parent) {
181-
let leftEdge = [curLevel[j].x, jParent.y, jParent.x - curLevel[j].x, 51];
192+
let leftEdge = [curLevel[j].x, jParent.y, jParent.x - curLevel[j].x, spacingY - 29];
182193
if (curLevel[j].lc === undefined)
183194
extrEdges[0].push(leftEdge);
184195
else
185196
edges[0].push(leftEdge);
186-
let rightEdge = [jParent.x, jParent.y, curLevel[j + 1].x - jParent.x, 51];
197+
let rightEdge = [jParent.x, jParent.y, curLevel[j + 1].x - jParent.x, spacingY - 29];
187198
if (curLevel[j + 1].lc === undefined)
188199
extrEdges[1].push(rightEdge);
189200
else

src/js/Splay.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { BinNode } from "./BinNode";
22
import { BST } from "./BST";
3-
class Splay extends BST {
3+
export class Splay extends BST {
44
splay(v) {
55
if (!v)
66
return null;

src/ts/AVL.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { BST } from "./BST"
22
import { BinNode } from "./BinNode"
33

4-
class AVL<T> extends BST<T> {
4+
export class AVL<T> extends BST<T> {
55

66
static avlBalanced<T>(x: BinNode<T>): boolean {
77
let balFac: number = BinNode.stature(x.lc) - BinNode.stature(x.rc);

src/ts/BinTree.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ export class BinTree<T> {
117117
return structInfo;
118118
}
119119

120+
// 常数设置:
121+
let spacingX: number = 80;
122+
if (this._size > 20) spacingX = 70;
123+
else if (this._size > 40) spacingX = 65;
124+
let spacingY: number = 80;
125+
if (this._root.height > 10) spacingY = 70;
126+
else if (this._root.height > 20) spacingY = 60;
127+
120128
// 逐层遍历
121129
this._root.y = 0;
122130
this._root.active = false;
@@ -127,7 +135,7 @@ export class BinTree<T> {
127135
levels.push([]);
128136
for (let j: number = 0; j < levels[i].length; j++) {
129137
let node: any = levels[i][j];
130-
let levelY = 80 * (i + 1);
138+
let levelY = spacingY * (i + 1);
131139
// 为外部节点添加一个外部节点孩子
132140
if (node.lc === undefined) {
133141
levels[i + 1].push({ x: node.x, y: levelY, parent: node });
@@ -136,7 +144,7 @@ export class BinTree<T> {
136144
node.active = false;
137145
node.visited = false;
138146

139-
let deltaX = (node.data.toString().length - 1) * 6;
147+
let deltaX = Math.max(0, node.data.toString().length - 2) * 6;
140148
// 为内部节点添加两个孩子
141149
if (node.lc) {
142150
node.lc.x = node.x - deltaX;
@@ -170,7 +178,7 @@ export class BinTree<T> {
170178
for (let j: number = 1; j < lastLevel.length; j++) {
171179
deltaL = deltaR;
172180
deltaR = j < lastLevel.length - 1 ? lastLevel[j + 1].x - lastLevel[j].x : 0;
173-
lastLevel[j].x = lastLevel[j - 1].x + 80
181+
lastLevel[j].x = lastLevel[j - 1].x + spacingX
174182
// if (lastLevel[j - 1].parent == lastLevel[j].parent) { lastLevel[j].x += deltaL }
175183
if (deltaL > 0) { lastLevel[j].x += deltaL }
176184
}
@@ -206,10 +214,10 @@ export class BinTree<T> {
206214
// 仅当父亲是内部节点时添加边
207215
let jParent: BinNode<T> = curLevel[j].parent;
208216
if (j < curLevel.length - 1 && jParent == curLevel[j + 1].parent) {
209-
let leftEdge: Array<number> = [curLevel[j].x, jParent.y, jParent.x - curLevel[j].x, 51];
217+
let leftEdge: Array<number> = [curLevel[j].x, jParent.y, jParent.x - curLevel[j].x, spacingY - 29];
210218
if (curLevel[j].lc === undefined) extrEdges[0].push(leftEdge);
211219
else edges[0].push(leftEdge);
212-
let rightEdge: Array<number> = [jParent.x, jParent.y, curLevel[j + 1].x - jParent.x, 51];
220+
let rightEdge: Array<number> = [jParent.x, jParent.y, curLevel[j + 1].x - jParent.x, spacingY - 29];
213221
if (curLevel[j + 1].lc === undefined) extrEdges[1].push(rightEdge);
214222
else edges[1].push(rightEdge);
215223
j += 2;

0 commit comments

Comments
 (0)