Skip to content

Commit 9268f53

Browse files
committed
Debug Black Height
1 parent 8e60dac commit 9268f53

23 files changed

+1137
-196
lines changed

README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
# TreePlayground
2-
A tool to generate, visualize and manipulate various kind of tree structures.
2+
## Tree Visualization Tool for Learning Data Structure and Algorithm.
3+
4+
Just visit https://hwc0919.github.io/TreePlayground/ and enjoy yourself.
35

4-
Just visit https://hwc0919.github.io/TreePlayground/index and enjoy yourself.<br/>
56
Or download contents in folder 'docs' to run offline.
67

78
(My personal server http://192.144.210.149/ may be shutdown shortly)
89

910
Program can run perfectly on Chrome and Firefox. Windows Edge is only functional when exploring online.
1011

11-
V1.4.4 made adaption for mobile devices, but still not recommended to use mobile phone for this program.
12+
V1.4.4 made adaption for mobile devices, but mobile use still not recommended.
13+
14+
可直接访问 https://hwc0919.github.io/TreePlayground/
1215

13-
可直接访问 https://hwc0919.github.io/TreePlayground/<br/>
1416
也可以下载文件夹"docs"中的内容本地运行.
1517

1618
(个人服务器 http://192.144.210.149/ 可能即将关闭)
@@ -19,6 +21,7 @@ Chorme和Firefox浏览器可以正常运行. Edge浏览器可以在线运行.
1921

2022
V1.4.4适配了移动端页面, 但仍不推荐在手机上运行.
2123

24+
2225
感谢
2326
[@Wasted-waste](https://github.com/Wasted-waste),
2427
[@B5DX](https://github.com/B5DX),
@@ -47,7 +50,7 @@ V1.4.4适配了移动端页面, 但仍不推荐在手机上运行.
4750

4851
为实现动态性, 使用了很多异步代码, 代码的可读性收到一些影响.
4952

50-
本人为前端新手, 对浏览器兼容性束手无策, 优化移动端界面也尚未提上日程, 应该在所有功能性代码完成之后考虑.
53+
红黑树将不实现异步接口, 因为染色操作可视化效果不明显, 而且在回调模式下增加内容太繁杂, 实在没有精力.
5154

5255
如果你发现了bug, 请在Issue里提出, 十分感谢.
5356

docs/bundle.js

Lines changed: 627 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/index.html

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
<meta name="author" content="NitroMelon">
88
<meta name="institution" content="Tsinghua University">
99
<meta name="git" content="https://github.com/hwc0919/TreePlayground">
10-
<meta name="website" content="https://192.144.210.149/">
1110
<link rel="Shortcut Icon" href="/favicon.ico">
1211
<title>TreePlayground - NitroMelon</title>
1312
</head>
@@ -50,16 +49,16 @@ <h4>Scale: <label v-text="commonParams.treeScale + '%'"></h4>
5049
<!-- Top Functional Node -->
5150
<top-binnode id="trvl-sequence" :data="topSequence" @top-build="onTopBuild" @top-insert="onTopInsert"
5251
@top-search="onTopSearch" @top-help="onTopHelp" @top-proper="onTopProper"></top-binnode>
53-
<div class="left-message">{{ messages.left }}</div>
54-
<div class="right-message">{{ messages.right }}</div>
52+
<div class="left-message" v-text="messages.left">加载中...</div>
53+
<div class="right-message" v-text="messages.right">Loading...</div>
5554
<!-- Internal Tree Nodes -->
5655
<binnode v-for="(node, ind) in structInfo.nodes" :node="node" :key="'node' + ind"
57-
:class="{'active-node': node.active, 'visited-node': node.visited, 'deprecated-node': node.deprecated}"
58-
@remove-below="onRemoveBelow" @remove-one="onRemoveOne" @intr-update="onIntrUpdate">
56+
:class="nodeColorClass(node.color)" @remove-below="onRemoveBelow" @remove-one="onRemoveOne"
57+
@intr-update="onIntrUpdate">
5958
</binnode>
6059
<!-- External Tree Nodes -->
61-
<extr-binnode v-show="showExtr" v-for="(node, ind) in structInfo.extrNodes" :node="node"
62-
:key="'extNode' + ind" @extr-insert="onExtrInsert">
60+
<extr-binnode v-for="(node, ind) in structInfo.extrNodes" :node="node" :key="'extNode' + ind"
61+
@extr-insert="onExtrInsert">
6362
</extr-binnode>
6463
<!-- Internal Tree Edges -->
6564
<div class="left-edge" v-for="e in structInfo.edges[0]"
@@ -68,16 +67,16 @@ <h4>Scale: <label v-text="commonParams.treeScale + '%'"></h4>
6867
:style="{'left': e[0]+'px', 'top': e[1]+'px', 'width': e[2]+'px', 'height': e[3]+'px'}"></div>
6968

7069
<!-- External Tree Edges -->
71-
<div v-show="showExtr" class="left-edge extr-edge" v-for="e in structInfo.extrEdges[0]"
70+
<div class="left-edge extr-edge" v-for="e in structInfo.extrEdges[0]"
7271
:style="{'left': e[0]+'px', 'top': e[1]+'px', 'width': e[2]+'px', 'height': e[3]+'px'}"></div>
73-
<div v-show="showExtr" class="right-edge extr-edge" v-for="e in structInfo.extrEdges[1]"
72+
<div class="right-edge extr-edge" v-for="e in structInfo.extrEdges[1]"
7473
:style="{'left': e[0]+'px', 'top': e[1]+'px', 'width': e[2]+'px', 'height': e[3]+'px'}"></div>
7574
</div>
7675
</div>
77-
<div id="footer" style="position: fixed; left: 5px; bottom: 5px;">Copyright @ 2020 NitroMelon
76+
<div id="footer" style="position: fixed; left: 5px; bottom: 5px;">Copyleft &copy; 2020 NitroMelon
7877
<a href="https://github.com/hwc0919/TreePlayground" target="_blank">Source Code</a>
7978
<a href="https://github.com/hwc0919/TreePlayground/issues" target="_blank">Bug report</a>
80-
<a href="https://github.com/hwc0919/TreePlayground/stargazers" target="_blank">&nearhk;Star</a>if you like it!
79+
<a href="https://github.com/hwc0919/TreePlayground/stargazers" target="_blank">&nearhk;Star</a> if you like it!
8180
</div>
8281
<script src="./bundle.js"></script>
8382
</body>

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "tree-playground",
3-
"version": "1.4.4",
4-
"description": "A tool to generate, visualize and manipulate various kind of tree structures.",
5-
"main": "",
3+
"version": "1.5.0",
4+
"description": "Tree Visualization Tool for Learning Data Structure and Algorithm.",
5+
"main": "./docs/index.html",
66
"scripts": {
77
"test": "echo \"Error: no test specified\" && exit 1",
88
"build": "webpack --config webpack.config.js"

src/app.js

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
import Vue from "../node_modules/vue/dist/vue.min"
1+
import Vue from "../node_modules/vue/dist/vue"
22
import IntrBinnode from "./components/binnode.vue"
33
import ExtrBinnode from "./components/extr-binnode.vue"
44
import TopBinnode from "./components/top-binnode.vue"
5-
import { BinNode } from "./js/BinNode"
5+
import { BinNode, RBColor, TreeUtil } from "./js/BinNode"
66
import { BinTree } from "./js/BinTree"
77
import { BST } from "./js/BST"
88
import { AVL } from "./js/AVL"
99
import { Splay } from "./js/Splay"
10+
import { RedBlack } from "./js/RedBlack"
1011

1112
var tp = new Vue({
1213
el: "#TreePlayground",
1314
data: {
14-
availTreeTypes: { "BinTree": true, "BST": true, "AVL": true, "Splay": true, "RedBlack": false },
15+
availTreeTypes: { "BinTree": true, "BST": true, "AVL": true, "Splay": true, "RedBlack": true },
1516
commonParams: {
1617
curTreeType: "BST", // Important : Always use as `this.curTreeType`.
1718
treeScale: 100, // in %
@@ -20,7 +21,7 @@ var tp = new Vue({
2021
messages: {
2122
left: "", right: ""
2223
},
23-
treeClassMap: { "BinTree": BinTree, "BST": BST, "AVL": AVL, "Splay": Splay },
24+
treeClassMap: { "BinTree": BinTree, "BST": BST, "AVL": AVL, "Splay": Splay, "RedBlack": RedBlack },
2425
trees: { "BinTree": null, "BST": null, "AVL": null, "Splay": null, "RedBlack": null },
2526
structInfo: {
2627
nodes: [],
@@ -228,13 +229,17 @@ var tp = new Vue({
228229
onRemoveOne(node) {
229230
if (this.isAnyLocked()) return false;
230231
this.showMessage(`Remove ${node.data}`);
231-
if ("Splay" === this.curTreeType) { // Exception : Deal with Splay
232+
if ("RedBlack" === this.curTreeType) { // TODO: No Vigor to write async version anymore using callback
233+
this.tree.remove(node.data); // Maybe change everything to await promise in the future.
234+
this.update();
235+
}
236+
else if ("Splay" === this.curTreeType) { // Exception : Deal with Splay
232237
this.alertAsync(`Step 1: Splay ${node.data}`, -1);
233238
node.active = true;
234239
setTimeout(() => {
235240
// Splay RM Step 1
236241
this.locks.rotateLock = true;
237-
this.splayAsync(node, (rootOrNull) => {
242+
this._splayAsync(node, (rootOrNull) => {
238243
if (rootOrNull === undefined) return false;
239244
if (rootOrNull === null) throw "Error in RemoveOne";
240245
let v = rootOrNull;
@@ -249,9 +254,9 @@ var tp = new Vue({
249254
node.active = false; node.deprecated = true;
250255
this.locks.trvlLock = true;
251256
this.alertAsync(`Step 2: Elevate Succ of ${node.data}`, -1);
252-
this.searchAsync(v.rc, v.data, (_, hot) => {
257+
this._searchAsync(v.rc, v.data, (_, hot) => {
253258
this.locks.rotateLock = true;
254-
this.splayAsync(hot, (newRoot) => {
259+
this._splayAsync(hot, (newRoot) => {
255260
// Splay RM Step 3
256261
this.alertAsync(`Step 3: Finally remove ${node.data}`, 2500);
257262
tree.reAttachAsLC(newRoot, v.lc);
@@ -282,7 +287,7 @@ var tp = new Vue({
282287
let succ = node.succ();
283288
node.deprecated = true;
284289
this.locks.trvlLock = true; // TODO : change to srchLock
285-
this.searchAsync(node, succ.data, () => { // assert res === true
290+
this._searchAsync(node, succ.data, () => { // assert res === true
286291
// RM Step 2: Swap with Succ
287292
this.alertAsync(`Step 2: Swap with Succ`, -1);
288293
this.update();
@@ -325,9 +330,9 @@ var tp = new Vue({
325330
setTimeout(() => {
326331
let interval = this.commonParams.interval;
327332
if (!AVL.avlBalanced(node))
328-
this.tree.rotateAt(BinNode.tallerChild(BinNode.tallerChild(node)));
333+
this.tree.rotateAt(TreeUtil.tallerChild(TreeUtil.tallerChild(node)));
329334
else interval = 0;
330-
this.tree.update_height(node);
335+
this.tree.updateHeight(node);
331336
this.update();
332337
node.active = true;
333338
setTimeout(() => {
@@ -365,15 +370,15 @@ var tp = new Vue({
365370
this.alertAsync(`Step 1: Search ${num}`, -1);
366371
this.locks.trvlLock = true;
367372
this.tree._hot = null; // Important: reset _hot before search
368-
this.searchAsync(this.tree.root(), num, (res, nodeOrHot) => {
373+
this._searchAsync(this.tree.root(), num, (res, nodeOrHot) => {
369374
let recentNode = null;
370375
// Exception : Deal with Splay
371376
if ("Splay" === this.curTreeType) { // Caution & Important & TODO : May need change
372377
this.alertAsync(nodeOrHot ? `Step 2: Splay at ${nodeOrHot.data}` : "", -1);
373378
// Wait & Splay & Insert in callback
374379
setTimeout(() => {
375380
this.locks.rotateLock = true;
376-
this.splayAsync(nodeOrHot, (rootOrNull) => {
381+
this._splayAsync(nodeOrHot, (rootOrNull) => {
377382
if (!res) {
378383
if (rootOrNull === undefined) return false; // `rotateLock` has been reset.
379384
this.alertAsync(`Final: ${num} Inserted`, 2500);
@@ -405,7 +410,7 @@ var tp = new Vue({
405410
this.update();
406411
if (this.topSequence.length === 0) {
407412
recentNode.active = true; // Caution: Mark recent active
408-
this.locks.trvlLock = false; return false;
413+
this.locks.trvlLock = false; return true;
409414
} else this.insertSequnceAsync();
410415
}, this.commonParams.interval);
411416
/* ----------------------------------------------------------------------------------------------------- */
@@ -420,20 +425,20 @@ var tp = new Vue({
420425
this.messages.left = `Search ${num}`;
421426

422427
this.tree._hot = null; // Important: reset _hot before search
423-
this.searchAsync(this.tree.root(), num, (res, nodeOrHot) => {
428+
this._searchAsync(this.tree.root(), num, (res, nodeOrHot) => {
424429
if (res) this.alertAsync(`${num} Found`);
425430
else Math.random() < 0.5 ? this.alertAsync(`${num} Not Found`) : this.alertAsync(`${num} 404`);
426431
if (this.curTreeType === "Splay") { // Exception & Important : Splay
427432
this.alertAsync(nodeOrHot ? `Splay at ${nodeOrHot.data}` : "", 2000);
428433
setTimeout(() => {
429434
this.locks.rotateLock = true;
430-
this.splayAsync(nodeOrHot);
435+
this._splayAsync(nodeOrHot);
431436
}, this.commonParams.interval);
432437
}
433438
});
434439
},
435440
// Search Async & Recur. Callback: (true, target) if found else (false, _hot)
436-
searchAsync(node, num, callback) { // Important: SET LOCK BEFORE START!
441+
_searchAsync(node, num, callback) { // Important: SET LOCK BEFORE START!
437442
if (!this.locks.trvlLock || !node) {
438443
this.locks.trvlLock = false;
439444
if (typeof callback === "function") callback(false, this.tree._hot);
@@ -452,12 +457,12 @@ var tp = new Vue({
452457
node.visited = true;
453458
if (num < node.data) node = node.lc;
454459
else node = node.rc;
455-
this.searchAsync(node, num, callback);
460+
this._searchAsync(node, num, callback);
456461
}, this.commonParams.interval);
457462
}
458463
},
459464
// Splay Async & Recur. Callback: (null) if !v, (undefined) if locked, (_root) if success
460-
splayAsync(v, callback) { // Important: SET `rotateLock` BEFORE START!
465+
_splayAsync(v, callback) { // Important: SET `rotateLock` BEFORE START!
461466
if (!v) {
462467
this.locks.rotateLock = false;
463468
if (typeof callback === "function") callback(null);
@@ -486,7 +491,7 @@ var tp = new Vue({
486491
this.update();
487492
v.active = true;
488493
setTimeout(() => {
489-
this.splayAsync(v, callback);
494+
this._splayAsync(v, callback);
490495
}, this.commonParams.interval);
491496
}
492497
},
@@ -503,7 +508,6 @@ var tp = new Vue({
503508
sequence.splice(last + 1);
504509
this.topSequence = sequence;
505510
},
506-
507511
/****************************************/
508512
/* Dragger */
509513
/****************************************/
@@ -572,7 +576,7 @@ var tp = new Vue({
572576
},
573577
checkNodeOrder(node, newV) {
574578
let pred, succ;
575-
let isLC = node.isLC || BinNode.isLC(node);
579+
let isLC = node.isLC || TreeUtil.isLC(node);
576580
if (node.lc === undefined) { // External nodes
577581
if (isLC === true && newV > node.parent.data ||
578582
isLC === true && (pred = node.parent.pred()) && newV < pred.data ||
@@ -590,6 +594,14 @@ var tp = new Vue({
590594
}
591595
return true;
592596
},
597+
598+
/****************************************/
599+
/* Others */
600+
/****************************************/
601+
nodeColorClass(color) {
602+
if (this.curTreeType === "RedBlack") return color == RBColor.Red ? "red-node" : "black-node";
603+
return "normal-color-node";
604+
},
593605
},
594606
computed: {
595607
tree: {
@@ -613,17 +625,14 @@ var tp = new Vue({
613625
let scale = this.treeScale / 100;
614626
return `transform:scale(${scale})`;
615627
},
616-
showExtr() {
617-
return true;
618-
},
619628
},
620629
watch: {
621-
tree: {
622-
handler() {
623-
console.log("Detect Change in tree.");
624-
},
625-
deep: true,
626-
},
630+
// tree: {
631+
// handler() {
632+
// console.log("Detect Change in tree.");
633+
// },
634+
// deep: true,
635+
// },
627636
commonParams: {
628637
handler() {
629638
localStorage.commonParams = JSON.stringify(this.commonParams);

src/components/binnode.vue

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<!-- Internal BinNode -->
22
<template>
3-
<div class="binnode intr-binnode" :style="{'left': node.x + 'px', 'top': node.y + 'px'}" @click="divOnClick">
3+
<div class="binnode intr-binnode" :style="{'left': node.x + 'px', 'top': node.y + 'px'}"
4+
:class="{'active-node': node.active, 'visited-node': node.visited, 'deprecated-node': node.deprecated}"
5+
@click="divOnClick">
46
<span v-show="!showInput" :title="title"
57
style="display: inline-block; width: 100%; height: 100%;">{{ node.data }}</span>
68
<label v-show="showRemoveOne" class="node-delete-btn node-upper-btn" title="remove one"
@@ -47,18 +49,20 @@
4749
},
4850
computed: {
4951
title() {
50-
return `height: ${this.node.height}\nsize: ${this.node.size()}\nnpl:${this.node.npl}`
52+
if (this.$parent.curTreeType === "RedBlack")
53+
return `blackH: ${this.node.blackH + 1}\nsize: ${this.node.size()}`
54+
return `height: ${this.node.height}\nsize: ${this.node.size()}`
55+
// return `npl:${this.node.npl}\nsize: ${this.node.size()}`
5156
},
5257
/* **************************************** */
5358
/* ************ Remove Buttons ************ */
5459
/* **************************************** */
5560
showRemoveBelow() { // Only BinTree or BST or Root!
56-
let curTreeType = this.$parent.commonParams.curTreeType;
61+
let curTreeType = this.$parent.curTreeType;
5762
return curTreeType === "BinTree" || curTreeType === "BST" || !this.node.parent;
5863
},
5964
showRemoveOne() { // Except BinTree and Splay!
60-
let curTreeType = this.$parent.commonParams.curTreeType;
61-
return curTreeType !== "BinTree";
65+
return this.$parent.curTreeType !== "BinTree";
6266
}
6367
}
6468
}

src/css/binnodes.css

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@
2222
z-index: 1;
2323
}
2424

25+
.normal-color-node {
26+
color: var(--normal-numcolor);
27+
}
28+
29+
.red-node {
30+
color: var(--red-color);
31+
}
32+
33+
.black-node {
34+
color: var(--black-color);
35+
}
36+
2537
.extr-binnode {
2638
z-index: 0;
2739
opacity: 0.1;
@@ -43,12 +55,10 @@
4355
}
4456

4557
.active-node {
46-
color: var(--active-color) !important;
4758
border-color: var(--active-color) !important;
4859
}
4960

5061
.visited-node {
51-
color: var(--visited-color) !important;
5262
border-color: var(--visited-color) !important;
5363
}
5464

src/css/index.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ html, body {
4040
--active-color: rgba(253, 121, 168, 1.0);
4141
--visited-color: gray;
4242
--deprecated-color: rgba(253, 203, 110, 1.0);
43+
--red-color: red;
44+
--black-color: black;
4345
}
4446

4547
#TreePlayground {

0 commit comments

Comments
 (0)