class Node {
constructor(size, weight) {
this.size = size;
this.weight = weight;
this.left = null;
this.right = null;
this.maxWeight = weight;
}
pathToSize(size) {
// Find a path to a node that would be the parent if
// a new node were added with the given size.
let child = size < this.size ? this.left : this.right;
if (child === null) return [this];
// Use recursion to add the rest of the path
return [this, ...child.pathToSize(size)];
}
findHeaviest() {
// Find a decendant that has a weight equal to the maxWeight of this node
if (this.weight === this.maxWeight) return this;
// As it is not this node that has that weight, it must be found
// in the left or the right subtree:
if (this.left !== null && this.left.maxWeight === this.maxWeight) {
return this.left.findHeaviest();
} else if (this.right !== null && this.right.maxWeight === this.maxWeight) {
return this.right.findHeaviest();
}
throw "tree is inconsistent";
}
findHeaviestWithMaxSize(size) {
let path = this.pathToSize(size);
// All nodes that have a size up to the given size are represented by this path, as follows:
// If a node on the path has a size that is not greater, then:
// that node itself and its complete left subtree is in that collection.
// The union of those collections has all nodes with a size up to the given size.
// So we will now find where in those collections we have the heaviest node.
let maxWeight = -Infinity; // The greatest weight we can find among valid nodes
let bestTree = null; // subtree with heaviest node
for (let node of path) {
if (node.size > size) continue; // skip this node
// Check the weight(!) of the current node:
if (node.weight > maxWeight) {
maxWeight = node.weight; // not its maxWeight!!
bestTree = node;
}
// Check the maxWeight(!) of the left subtree
node = node.left;
if (node !== null && node.maxWeight > maxWeight) {
maxWeight = node.maxWeight;
bestTree = node;
}
}
if (bestTree === null) return null; // there is no such node
// Get the node with that maxWeight in the identified node/subtree
if (bestTree.weight === maxWeight) return bestTree;
return bestTree.findHeaviest();
}
toString(node) {
// Provide a string representation of this tree
let left = [];
let right = [];
if (this.left !== null) left = this.left.toString(node).split("\n");
if (this.right !== null) right = this.right.toString(node).split("\n");
let leftWidth = left[0]?.length || 0;
let rightWidth = right[0]?.length || 0;
let width = leftWidth + 5 + rightWidth;
let lines = Array.from({length: Math.max(left.length, right.length)}, (_, i) =>
(left[i] || " ".repeat(leftWidth)) + " " + (right[i] || ""));
if (lines.length) {
lines.unshift(
(leftWidth ? left[0].replace(/\S.*/, "┌").padEnd(leftWidth+2, "─") : " ".repeat(leftWidth+2))
+ "┴┘└"[!leftWidth * 2 + !rightWidth]
+ (rightWidth ? right[0].replace(/.*\S/, "┐").padStart(rightWidth+2, "─") : " ".repeat(rightWidth+2))
);
lines.unshift("│".padStart(leftWidth+3).padEnd(width));
}
lines.unshift((String(this.weight).padStart(4, "0") + "g").padStart(leftWidth+5).padEnd(width));
lines.unshift((String(this.size).padStart(4, "0") + "m").padStart(leftWidth+5).padEnd(width));
lines.unshift((node === this ? "▼" : "│").padStart(leftWidth+3).padEnd(width));
return lines.join("\n");
}
}
class Tree {
constructor() {
this.root = null;
// Only needed for this demo:
// for exporting the insertions that are made
this.array = [];
}
add(size, weight) {
this.array.push([size, weight]);
if (this.root === null) {
this.root = new Node(size, weight);
return;
}
let path = this.root.pathToSize(size);
let parent = path.pop();
if (size < parent.size) {
parent.left = new Node(size, weight);
} else {
parent.right = new Node(size, weight);
}
// Adjust weight of ancestors
while (parent !== undefined && parent.maxWeight < weight) {
parent.maxWeight = weight;
parent = path.pop();
}
}
toString(markedNode) {
if (this.root === null) return "";
return this.root.toString(markedNode);
}
findHeaviestWithMaxSize(size) {
if (this.root === null) return null;
return this.root.findHeaviestWithMaxSize(size);
}
static from(arr) {
let tree = new Tree;
for (let pair of arr) {
tree.add(...pair);
}
return tree;
}
}
let tree = new Tree;
// I/O handling below:
let inpJson = document.getElementById("json");
let inpSize = document.getElementById("size");
let inpWeight = document.getElementById("weight");
let btnCreate = document.getElementById("create");
let output = document.querySelector("pre");
function refresh() {
let node = tree.findHeaviestWithMaxSize(+inpSize.value);
output.textContent = tree.toString(node);
json.value = JSON.stringify(tree.array);
}
function load() {
let array;
try {
array = JSON.parse(json.value);
} catch {
return;
}
tree = Tree.from(array);
refresh();
}
inpJson.addEventListener("input", load);
inpSize.addEventListener("input", refresh);
btnCreate.addEventListener("click", function() {
tree = randomTree();
refresh();
});
function randBetween(a, b) {
return Math.floor(Math.random() * (b - a + 1) + a);
}
function randPair() {
return [randBetween(0, 10), randBetween(0, 10)];
}
function randomTree() {
let array = Array.from({length: 10}, randPair);
return Tree.from(array);
}
load();
#json { width: 100% }
#size { width: 3em }
JSON with [size, weight] pairs, in insertion order:<br>
<input id="json" value="[[6,8],[4,1],[8,10],[5,5],[7,5],[9,6],[5,9],[6,7],[8,6]]"><br>
<button id="create">Randomise</button><br>
<hr>
Find heaviest node with size at most: <input id="size" type="number" value="5">m<br>
<pre>
</pre>
n
и количество запросов? - person Abhinav Mathur   schedule 19.01.2021