Skip to content

Commit b2026d7

Browse files
committed
Add ProperSequence building function. Improve user experience.
Tree samples are randomly generated now.
1 parent ada0d68 commit b2026d7

File tree

12 files changed

+434
-156
lines changed

12 files changed

+434
-156
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@ To run the program, you only need to download contents in folder 'dist'.
55

66
No compatibility testing. Functions are only guaranteed in Chrome.
77

8-
若要运行程序, 只需要下载文件夹"dist"中的内容即可.
8+
若要运行程序, 只需要下载文件夹"dist"中的内容即可(项目容量很小, 也可全部下载).
99

1010
没有测试兼容性, 只能保证chorme浏览器下正常运行.
1111

12+
感谢
13+
[@Wasted-waste](https://github.com/Wasted-waste),
14+
[@B5DX](https://github.com/B5DX),
15+
[@lijingwei233](https://github.com/lijingwei233),
16+
[@baijiangying](https://github.com/baijiangying),
17+
等同学对调试的帮助的建议
1218

1319
## Objective
1420
I create this project for the purpose of helping those who are learning BBST in Data Structure course.

dist/TreePlayground.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<div id="TreePlayground" @mousemove='onTPMouseMove($event)' @touchmove='onTPMouseMove($event)'>
1515
<!-- Top Toolbar -->
1616
<div class="top-toolbar">
17-
<button class="btn btn-primary top-toolbar-item" type="button" @click="loadSampleTree">Sample</button>
17+
<button class="btn btn-primary top-toolbar-item" type="button" @click="loadSampleTree(); update()">Sample</button>
1818
<button class="btn btn-primary top-toolbar-item" type="button" @click="traversal(0)">先序遍历</button>
1919
<button class="btn btn-primary top-toolbar-item" type="button" @click="traversal(1)">中序遍历</button>
2020
<button class="btn btn-primary top-toolbar-item" type="button" @click="traversal(2)">后序遍历</button>
@@ -38,22 +38,22 @@ <h4>Scale: <label v-text="commonParams.treeScale + '%'"></h4>
3838
</div>
3939
<!-- Tree Visualization Part -->
4040
<div class="tree" ref="tree" :style="adjustScale" style="transform-origin: top;"
41-
@mousedown.self="onTreeMouseDown($event)" @mouseup.self="onTreeMouseLeave($event)"
42-
@touchstart.self="onTreeMouseDown($event)" @touchend.self="onTreeMouseLeave($event)">
41+
@mousedown.self="onTreeMouseDown" @mouseup.self="onTreeMouseLeave"
42+
@touchstart.self="onTreeMouseDown" @touchend.self="onTreeMouseLeave">
4343
<!-- Top Functional Node -->
4444
<top-binnode id="trvl-sequence" :data="topSequence" @top-build="onTopBuild" @top-insert="onTopInsert"
45-
@top-search="onTopSearch"></top-binnode>
45+
@top-search="onTopSearch" @top-help="onTopHelp" @top-proper="onTopProper"></top-binnode>
4646
<div class="left-message">{{ messages.left }}</div>
4747
<div class="right-message">{{ messages.right }}</div>
4848
<!-- Internal Tree Nodes -->
4949
<binnode v-for="(node, ind) in structInfo.nodes" :node="node" :key="'node' + ind"
5050
:class="{'active-node': node.active, 'visited-node': node.visited}"
51-
@remove-below="onRemoveBelow($event)" @remove-one="onRemoveOne($event)"
52-
@intr-update="onIntrUpdate($event)">
51+
@remove-below="onRemoveBelow" @remove-one="onRemoveOne"
52+
@intr-update="onIntrUpdate">
5353
</binnode>
5454
<!-- External Tree Nodes -->
5555
<extr-binnode v-for="(node, ind) in structInfo.extrNodes" :node="node" :key="'extNode' + ind"
56-
@extr-insert="onExtrInsert($event)">
56+
@extr-insert="onExtrInsert">
5757
</extr-binnode>
5858
<!-- Internal Tree Edges -->
5959
<div class="left-edge" v-for="e in structInfo.edges[0]"

dist/bundle.js

Lines changed: 164 additions & 63 deletions
Large diffs are not rendered by default.

src/app.js

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ var vm = new Vue({
4242
console.log("Load default tree.")
4343
this.loadSampleTree();
4444
}
45+
this.reset();
4546
},
4647
reset() {
4748
console.log("Reset");
@@ -62,9 +63,8 @@ var vm = new Vue({
6263
loadFromHistory() { },
6364
loadSampleTree() {
6465
this.tree = this.curTreeClass.genSampleTree();
65-
this.update();
6666
},
67-
alertAsync(message, time = 1000) {
67+
alertAsync(message, time = 1500) {
6868
this.messages.right = message;
6969
let tag = ++this.alertTag;
7070
setTimeout((e = tag) => {
@@ -111,48 +111,36 @@ var vm = new Vue({
111111

112112
// Events Handlers
113113
onIntrUpdate(args) { // Internal node requests for value update
114-
console.log("onIntrUpdate");
115114
let node = args[0];
116115
let updation = args[1];
116+
let successMessage = `Change ${node.data} to ${updation}`;
117117
if (this.curTreeType !== "BinTree") {
118118
if (this.tree.search(updation)) {
119-
alert("Already exists!");
120-
return false;
121-
} else if (BinNode.isLC(node) && updation > node.parent.data ||
122-
BinNode.isRC(node) && updation < node.parent.data ||
123-
node.lc && updation < node.lc.data ||
124-
node.rc && updation > node.rc.data) {
125-
alert("Must maintain order.");
119+
this.alertAsync(`${updation} Exists!`);
126120
return false;
127121
}
122+
if (!this.checkNodeOrder(node, updation)) return false;
128123
}
129124
node.data = updation;
130125
this.update();
131-
},
126+
this.messages.left = successMessage;
127+
}, // TODO: active newly updated node. Update before and after every action.
132128
onExtrInsert(args) { // External node requests for value insertion
133-
console.log("onExtrInsert");
134129
let node = args[0];
135130
let insertion = args[1];
136131
let curTreeType = this.curTreeType;
137132

138133
if (curTreeType === "Splay") {
139-
alert("Can't insert at external nodes in SplayTree.");
134+
this.alertAsync("Can't insert at external nodes in SplayTree.", 3000);
140135
return false;
141136
}
142137
if (curTreeType !== "BinTree") {
143138
if (this.tree.search(insertion)) { // Decline duplicate
144-
alert("Already exists!");
145-
return false;
146-
}
147-
// pred and succ of parent
148-
let pred, succ;
149-
if (node.isLC === true && insertion > node.parent.data ||
150-
node.isLC === true && (pred = node.parent.pred()) && insertion < pred.data ||
151-
node.isLC === false && insertion < node.parent.data ||
152-
node.isLC === false && (succ = node.parent.succ()) && insertion > succ.data) {
153-
alert("Must maintain order.");
139+
this.alertAsync(`${insertion} Exists!`);
154140
return false;
155141
}
142+
// check new order
143+
if (!this.checkNodeOrder(node, insertion)) return false;
156144
}
157145
var updateH;
158146
if (curTreeType === "BinTree" || curTreeType === "BST")
@@ -169,15 +157,28 @@ var vm = new Vue({
169157

170158
if (curTreeType === "AVL") {
171159
this.tree.search(insertion); // locate _hot
172-
this.tree.solveInsertUnbalance();
160+
this.tree.solveInsertUnbalance(); // TODO: change to async
173161
}
174162
this.update();
163+
this.messages.left = `Insert ${insertion}`;
164+
},
165+
checkNodeOrder(node, newV) {
166+
let pred, succ;
167+
let isLC = node.isLC || BinNode.isLC(node);
168+
if (isLC === true && newV > node.parent.data ||
169+
isLC === true && (pred = node.parent.pred()) && newV < pred.data ||
170+
isLC === false && newV < node.parent.data ||
171+
isLC === false && (succ = node.parent.succ()) && newV > succ.data ||
172+
node.lc && newV < node.lc.data || node.rc && newV > node.rc.data) {
173+
this.alertAsync("Must maintain order.", 2500);
174+
return false;
175+
} return true;
175176
},
176177
// Remove whole subtree
177178
onRemoveBelow(node) {
178179
this.tree.removeBelow(node);
179180
this.update();
180-
this.alertAsync(`Remove Below ${node.data}`, 1000);
181+
this.alertAsync(`Remove Below ${node.data}`);
181182
},
182183
// Remove one node
183184
onRemoveOne(node) {
@@ -189,7 +190,7 @@ var vm = new Vue({
189190
}
190191
else if (0) { }
191192
this.update();
192-
this.alertAsync(`Remove ${node.data}`, 1000);
193+
this.alertAsync(`Remove ${node.data}`);
193194
},
194195
// Proper Rebuild
195196
onTopBuild(sequence) {
@@ -198,6 +199,10 @@ var vm = new Vue({
198199
this.tree.buildFromBinSequence(sequence);
199200
this.update();
200201
this.messages.left = "真二叉树层次序列构建";
202+
203+
this.curTreeClass.checkValidity(this.tree, (res, message) => {
204+
if (!res) this.alertAsync(message, 3000);
205+
})
201206
},
202207
// Insert sequence
203208
onTopInsert(sequence) {
@@ -229,6 +234,18 @@ var vm = new Vue({
229234
else Math.random() < 0.5 ? this.alertAsync("Not Found") : this.alertAsync("404");
230235
});
231236
},
237+
onTopHelp(message) {
238+
this.alertAsync(message, 5000);
239+
},
240+
// Proper Binary Tree Sequence
241+
onTopProper() {
242+
let sequence = BinTree.properTraversal(this.tree.root());
243+
for (let i = 0; i < sequence.length; i++) sequence[i] = sequence[i] ? sequence[i].data : null;
244+
let last = sequence.length - 1;
245+
while (sequence[last] === null) last--;
246+
sequence.splice(last + 1);
247+
this.topSequence = sequence;
248+
},
232249
searchAsync(node, num, callback) {
233250
if (!this.trvlParams.lock || !node) {
234251
this.trvlParams.lock = false;
@@ -293,7 +310,7 @@ var vm = new Vue({
293310
},
294311
strToArr(str) {
295312
str = str.trim();
296-
if (str === "") return false;
313+
if (str === "") return null;
297314
let arr = str.split(/,|/);
298315
for (let i = 0; i < arr.length; i++) {
299316
arr[i] = this.assertNumber(arr[i]);
@@ -306,7 +323,6 @@ var vm = new Vue({
306323
get() { return this.trees[this.curTreeType]; },
307324
set(newTree) {
308325
this.trees[this.curTreeType] = newTree;
309-
this.update();
310326
}
311327
},
312328
curTreeType: {

src/components/components.js

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Vue from "../js/vue"
2-
import { BinNode } from "../js/BinNode";
32

3+
4+
// Input Component with Self-ajusted-width
45
Vue.component('binnode-input', {
56
data: function () {
67
return {
@@ -24,12 +25,15 @@ Vue.component('binnode-input', {
2425
},
2526
watch: {
2627
value() {
27-
this.width = this.$refs.widthIndicator.offsetWidth;
28+
process.nextTick(() => {
29+
this.width = this.$refs.widthIndicator.offsetWidth;
30+
})
2831
}
2932
}
3033
})
3134

3235

36+
// Internal BinNode
3337
Vue.component('binnode', {
3438
props: ['node'],
3539
data() {
@@ -87,7 +91,7 @@ Vue.component('extr-binnode', {
8791
props: ['node'],
8892
template:
8993
`<div class="binnode extr-binnode" :style="{'left': node.x + 'px', 'top': node.y + 'px'}" @click="divOnClick">
90-
<binnode-input ref="input" v-show="showInput" v-model="insertion" @blur.native="showInput = false"
94+
<binnode-input ref="input" v-show="showInput" v-model="insertion" @blur="showInput = false"
9195
@keyup.enter.native="emitExtrInsert">
9296
</binnode-input>
9397
</div>
@@ -102,18 +106,24 @@ Vue.component('extr-binnode', {
102106
divOnClick() {
103107
if (this.showInput === true) return false;
104108
this.showInput = true;
105-
setTimeout(() => {
109+
process.nextTick(() => {
106110
this.$refs.input.forceFocus();
107-
}, 1);
111+
})
108112
}
109113
}
110114
});
111115

112-
{/* */ }
113116

117+
// Top Funtion Node
114118
Vue.component('top-binnode', {
115119
data() {
116-
return { sequence: this.data.toString(), showInput: false };
120+
return {
121+
sequence: this.data.toString(), showInput: false, helpInd: -1,
122+
helpMessages: ["B: 真二叉树构建, 逗号分隔. 例如 1,,2,,3 对应 {1: [null, 2: [null, 3]]}",
123+
"I: (回车)输入序列依次插入, 逗号分隔. 上限为666..666(具体几个不告诉你)",
124+
"S: 输入单个数值进行搜索",
125+
"P: 生成真二叉树层次遍历序列(复制下来就可以随时重建啦)"]
126+
};
117127
},
118128
props: ['data'],
119129
template:
@@ -123,32 +133,34 @@ Vue.component('top-binnode', {
123133
@click.stop="emitTopBuild"><i>B</i></label>
124134
<label v-show="showTopInsert" class="top-insert-btn node-upper-btn" title="按次序插入" @click.stop="emitTopInsert"><i>I</i></label>
125135
<label v-show="showTopSearch" class="top-search-btn node-upper-btn" title="查找单个数值" @click.stop="emitTopSearch"><i>S</i></label>
136+
<label class="top-help-btn node-left-btn" title="help" @click.stop="emitTopHelp"><i>?</i></label>
137+
<label class="top-proper-btn node-upper-btn" title="生成真二叉序列" @click.stop="$emit('top-proper')"><i>P</i></label>
126138
<binnode-input ref="input" v-show="showInput" v-model="sequence" @blur="showInput=false" @keyup.enter.native="emitTopInsert">
127139
</binnode-input>
128140
</div>`,
129141
methods: {
130142
divOnClick() {
131143
if (this.showInput === true) return false;
132144
this.showInput = true;
133-
setTimeout(() => {
145+
process.nextTick(() => {
134146
this.$refs.input.forceFocus();
135-
}, 1);
147+
})
136148
},
137149
emitTopBuild() {
138-
if (this.$parent.tree && this.$parent.tree.root())
139-
if (!confirm("Overwrite current tree?")) return false;
140-
150+
if (this.sequence === "") return false;
141151
let sequence = this.$parent.strToArr(this.sequence);
142-
this.sequence = sequence.toString();
143-
144-
if (sequence[0] === null) {
152+
if (!sequence || sequence[0] === null) {
145153
alert("Empty Root!");
146154
return false;
147155
}
156+
if (this.$parent.tree && this.$parent.tree.root())
157+
if (!confirm("Overwrite current tree?")) return false;
158+
this.sequence = sequence.toString();
148159
this.$emit('top-build', sequence);
149160
},
150161
emitTopInsert() {
151162
let sequence = this.$parent.strToArr(this.sequence);
163+
if (!sequence || sequence.length === 0) return false;
152164
this.sequence = sequence.toString();
153165
this.$emit('top-insert', sequence);
154166
},
@@ -157,6 +169,14 @@ Vue.component('top-binnode', {
157169
if (num === null) return false;
158170
this.sequence = num.toString();
159171
this.$emit('top-search', num);
172+
},
173+
emitTopHelp() {
174+
this.helpInd = ++this.helpInd % this.helpMessages.length;
175+
let enableHelp = [this.showTopBuild, this.showTopInsert, this.showTopSearch, true]
176+
let failSafe = 0;
177+
while (!enableHelp[this.helpInd] && failSafe++ < 5)
178+
this.helpInd = ++this.helpInd % this.helpMessages.length;
179+
if (enableHelp[this.helpInd]) this.$emit('top-help', this.helpMessages[this.helpInd]);
160180
}
161181
},
162182
computed: {

0 commit comments

Comments
 (0)