A célunk egy olyan bináris fa létrehozása, amelynek levelei a tömb elemeit tárolják, a belső csomópontok pedig bizonyos intervallumok eredményeit (pl. összegeket) tartalmazzák.
[36] / \ [9] [27] / \ / \ [4] [5] [16] [11] / \ / \ [1] [3] [7] [9]
build(startIndex, endIndex, parent = null) {
const originalLength = endIndex - startIndex + 1;
let size = 1;
while (size < originalLength) size *= 2;
let padded = this.numbers.slice(startIndex, endIndex + 1);
while (padded.length < size) padded.push(0);
const buildRecursive = (l, r, parent) => {
const isLeafPadding = l >= originalLength;
if (l === r) {
return new Node(parent, padded[l], [l, l], null, isLeafPadding);
}
let mid = Math.floor((l + r) / 2);
let leftChild = buildRecursive(l, mid, null);
let rightChild = buildRecursive(mid + 1, r, null);
let value = leftChild.value + rightChild.value;
let isPadding = leftChild.isPadding && rightChild.isPadding;
let node = new Node(parent, value, [l, r], [leftChild, rightChild], isPadding);
leftChild.parent = node;
rightChild.parent = node;
return node;
};
return buildRecursive(0, size - 1, parent);
}
Vesszővel elválasztva add meg az értékeket! Pl.: 9,15,12,68,91
A szegmensfa egyik legnagyobb előnye, hogy gyorsan tudunk intervallum lekérdezéseket végrehajtani. Például egy adott tartomány összegét kérdezhetjük le.
query(node, L, R) {
if (!node || node.interval[1] < L || node.interval[0] > R) {
return 0;
}
if (L <= node.interval[0] && node.interval[1] <= R) {
return node.value;
}
let leftSum = this.query(node.children[0], L, R);
let rightSum = this.query(node.children[1], L, R);
return leftSum + rightSum;
}
Ezzel a módszerrel gyorsan, O(log N)
idő alatt végezhetünk intervallum
lekérdezéseket.
Vesszővel elválasztva add meg az értékeket! Pl.: 9,15,12,68,91
A szegmensfa segítségével gyorsan frissíthetünk egy adott indexű elemet, és az érintett csomópontok újra kiszámításra kerülnek.
update(index, newValue) {
let node = this.root;
while (node.interval[0] !== node.interval[1]) {
let mid = Math.floor((node.interval[0] + node.interval[1]) / 2);
if (index <= mid) {
node = node.children[0];
} else {
node = node.children[1];
}
}
let difference = newValue - node.value;
node.value = newValue;
let parent = node.parent;
while (parent !== null) {
parent.value += difference;
parent = parent.parent;
}
}
Az update művelet is O(log N)
időben fut, mivel csak egy útvonalon kell frissíteni
az
értékeket.
Vesszővel elválasztva add meg az értékeket! Pl.: 9,15,12,68,91
A szegmensfa lehetővé teszi új elemek beszúrását és a megfelelő csomópontok frissítését.
padding
levél:
padding
levélbe beírjuk az új értéket.padding
jelzést, ha szükséges.padding
levél:
insert(value) {
const findFirstPaddingLeaf = (node) => {
if (!node.children) {
return node.isPadding ? node : null;
}
return findFirstPaddingLeaf(node.children[0]) || findFirstPaddingLeaf(node.children[1]);
};
let targetLeaf;
if (this.root.children) {
targetLeaf = findFirstPaddingLeaf(this.root.children[1]);
} else {
targetLeaf = findFirstPaddingLeaf(this.root);
}
if (targetLeaf) {
const difference = value - targetLeaf.value;
targetLeaf.value = value;
targetLeaf.isPadding = false;
this.numbers[targetLeaf.interval[0]] = value;
let parent = targetLeaf.parent;
while (parent !== null) {
parent.value += difference;
const [leafStart, leafEnd] = targetLeaf.interval;
const [parentStart, parentEnd] = parent.interval;
if (parent.isPadding && leafStart >= parentStart && leafEnd <= parentEnd) {
parent.isPadding = false;
}
parent = parent.parent;
}
} else {
const oldRoot = this.root;
const newIndex = this.numbers.length;
this.numbers.push(value);
let newLeaf = new Node(null, value, [newIndex, newIndex], null, false);
let branch = newLeaf;
const isPowerOfTwo = (n) => (n & (n - 1)) === 0;
let depth = 1;
while (!isPowerOfTwo(this.numbers.length)) {
let parent = new Node(null, branch.value, [newIndex, newIndex], [branch, null], false);
branch.parent = parent;
branch = parent;
depth++;
this.numbers.length++;
}
const newRoot = new Node(
null,
oldRoot.value + branch.value,
[0, newIndex],
[oldRoot, branch],
false
);
oldRoot.parent = newRoot;
branch.parent = newRoot;
this.root = newRoot;
}
}
Vesszővel elválasztva add meg az értékeket! Pl.: 9,15,12,68,91