二叉树实现红点管理
游戏中的红点系统,记录下使用二叉树更新和查询红点信息。
红点的信息是明显包含一个树形结构,这里使用二叉树,其中左节点表示孩子节点,右节点表示兄弟节点。
红点信息以数组形式表示,例如一组信息 [活动, 限时活动, 具体活动]
。
红点的更新与查询可分为:添加红点、删除红点、查询红点。
主要代码如下,注意二叉树递归遍历的细节
结构定义
private redDotRoot: any; // 根节点
private redDotType: any = -1;
constructor(type: any = -1) {
this.redDotType = type;
this.redDotRoot = this.createNode(type);
}
private createNode(type: any, left: any = null, right: any = null): any {
let node = {
type: type,
left: left,
right: right,
}
return node;
}
查询红点
/**
* 查询是否为红点
* @param args
* @returns
*/
public IsRedDot(args: any[]): boolean {
if (!args || !args.length) return false;
if (!this.redDotRoot) this.redDotRoot = this.createNode(this.redDotType);
return this.queryNode(this.redDotRoot, args, 0) > 0;
}
// -1 没有该结点
// 0 有该结点, 不是红点
// 1 是红点
private queryNode(node: any, args: any[], index: number): number {
let nextNode = node.left;
while (nextNode) {
if (nextNode.type == args[index]) {
if (index + 1 == args.length) { // 查询最后一个
return nextNode.left != null ? 1 : 0;
} else {
return this.queryNode(nextNode, args, index + 1);
}
}
nextNode = nextNode.right;
}
return -1;
}
添加红点
/**
* 添加红点信息
* @param args 红点路径, 示例:[活动, 活动兑换, 彩灯, 具体兑换任务]
* @param isCheck 增加红点前是否检查重复添加, 有些地方代码可能会Add多次. 建议在逻辑清晰的前提下, 设为false
*/
public AddRedDot(args: any[], isCheck: boolean = false): void {
if (!args || !args.length) return;
if (!this.redDotRoot) this.redDotRoot = this.createNode(this.redDotType);
// 判断是否重复添加
if (isCheck && this.queryNode(this.redDotRoot, args, 0) >= 0) {
return; // 已有该结点不需要重复添加
}
this.addNode(this.redDotRoot, args, 0);
}
private addNode(node: any, args: any[], index: number): void {
if (index + 1 == args.length) {
// 最后一个无需在遍历
let newNode = this.createNode(args[index], null, node.left);
node.left = newNode;
return;
}
let prevNode = node;
let nextNode = node.left;
while (nextNode) {
if (nextNode.type == args[index]) {
if (prevNode !== node) { // 使当前查询路径在第一个(小优化)
prevNode.right = nextNode.right;
nextNode.right = node.left;
node.left = nextNode;
}
this.addNode(nextNode, args, index + 1);
return;
}
nextNode = nextNode.right;
}
nextNode = this.createNode(args[index], null, node.left);
node.left = nextNode;
this.addNode(nextNode, args, index + 1);
}
删除红点
/**
* 删除红点信息
* @param args 同上, 删除树中最后一个arg
*/
public RemoveRedDot(args: any[]): void {
if (!args) return;
if (!this.redDotRoot) this.redDotRoot = this.createNode(this.redDotType);
let tree = this.removeNode(this.redDotRoot, args, 0);
// let tree = this.removeNode1(null, this.redDotRoot, args, 0, args.length);
this.clearTree(tree); // 清理需要删除的红点树的所有引用
}
private removeNode(node: any, args: any[], index: number): any {
let prevNode = node;
let nextNode = node.left;
while (nextNode) {
if (nextNode.type == args[index]) {
if (prevNode !== node) { // 使当前查询路径在第一个(小优化)
prevNode.right = nextNode.right;
nextNode.right = node.left;
node.left = nextNode;
}
if (index + 1 == args.length) { // 查找结束
return nextNode;
} else {
let ret = this.removeNode(nextNode, args, index + 1);
if (ret === null) return null;
if (nextNode.left === ret) {
if (ret.right === null) {
ret = nextNode;
} else {
nextNode.left = ret.right;
ret.right = null;
}
}
return ret;
}
}
prevNode = nextNode;
nextNode = nextNode.right;
}
return null;
}
清理树结构
/**清理树结构 */
public Clear(): void {
this.clearTree(this.redDotRoot);
this.redDotRoot = null;
}
private clearTree(node: any): void {
if (!node) return;
this.clearTree(node.left);
this.clearTree(node.right);
node.left = null;
node.right = null;
}