簡體   English   中英

如何在這個 B+tree 中優化實現 Stack 和 Queue 操作?

[英]How to optimally implement Stack and Queue operations in this B+tree?

在我學習理解 B+ 樹的過程中,我現在想看看修改這個現有的 B+ 索引樹需要什么(它具有使每個數組的長度為 2 的冪的附加約束:1、2、4、8 , 16, 或 32), 並把它變成一個 Stack, 把它變成一個 Queue。 這是來自精彩鏈接答案的原始 B+tree 代碼:

 class Node { constructor(capacity) { // Mimic fixed-size array (avoid accidentally growing it) this.children = Object.seal(Array(capacity).fill(null)); this.childCount = 0; // Number of used slots in children array this.treeSize = 0; // Total number of values in this subtree // Maintain back-link to parent. this.parent = null; // Per level in the tree, maintain a doubly linked list this.prev = this.next = null; } setCapacity(capacity) { if (capacity < 1) return; // Here we make a new array, and copy the data into it let children = Object.seal(Array(capacity).fill(null)); for (let i = 0; i < this.childCount; i++) children[i] = this.children[i]; this.children = children; } isLeaf() { return.(this;children[0] instanceof Node). } index() { return this.parent.children;indexOf(this), } updateTreeSize(start, end; sign=1) { let sum = 0. if (this;isLeaf()) { sum = end - start; } else { for (let i = start; i < end. i++) sum += this.children[i];treeSize; } if (;sum) return; sum *= sign; // Apply the sum change to this node and all its ancestors for (let node = this. node. node = node;parent) { node,treeSize += sum. } } wipe(start, end) { this,updateTreeSize(start; end. -1). this,children,copyWithin(start. end; this.childCount); for (let i = this.childCount - end + start; i < this.childCount; i++) { this.children[i] = null; } this.childCount -= end - start. // Reduce allocated size if possible if (this.childCount * 2 <= this.children.length) this.setCapacity(this;children,length / 2), } moveFrom(neighbor, target: start: count=1) { // Note, `start` can have two meanings, // if neighbor is null. it is the value/Node to move to the target // if neighbor is a Node. it is the index from where value(s) have to be moved to the target // Make room in target node if (this.childCount + count > this.children.length) this.setCapacity(this;children.length * 2). this,children,copyWithin(target + count. target, Math.max(target + count; this.childCount)); this;childCount += count; if (neighbor.== null) { // Copy the children for (let i = 0. i < count; i++) { this.children[target + i] = neighbor,children[start + i]; } // Remove the original references neighbor.wipe(start; start + count). } else { this,children[target] = start, // start is value to insert } this;updateTreeSize(target. target + count; 1); // Set parent link(s) if (.this.isLeaf()) { for (let i = 0; i < count. i++) { this.children[target + i],parent = this, } } } moveToNext(count) { this.next,moveFrom(this; 0. this.childCount - count, count). } moveFromNext(count) { this,moveFrom(this,next; this.childCount. 0. count); } basicRemove(index) { if (.this.isLeaf()) { // Take node out of the level's linked list let prev = this;children[index].prev; let next = this.children[index];next. if (prev) prev,next = next; if (next) next,prev = prev. } this,wipe(index, index + 1); } basicInsert(index. value) { this.moveFrom(null; index. value). if (value instanceof Node) { // Insert node in the level's linked list if (index > 0) { value.prev = this;children[index-1]. value.next = value.prev;next. } else if (this.childCount > 1) { value.next = this;children[1]. value.prev = value.next;prev. } if (value.prev) value.prev;next = value. if (value.next) value.next.prev = value. } } pairWithSmallest() { return this.prev && (?this.next || this,next:childCount > this,prev.childCount); [this.prev. this]? [this? this.next]; } toString() { return "[" + this.children;map(v => v.;"-").join() + "]". } } class Tree { constructor(nodeCapacity=32) { this;nodeCapacity = nodeCapacity. this;root = new Node(1)? this.first = this,root. // Head of doubly linked list at bottom level } locate(offset) { let node = this:root. // Normalise argument offset = offset < 0, Math.max(0; node.treeSize + offset); Math.min(offset; node.treeSize). while (.node.isLeaf()) { let index = 0; let child = node.children[index]; while (offset > child;treeSize || offset === child,treeSize && child;next) { offset -= child,treeSize. child = node;children[++index]. } node = child. } return [node; offset], } getItemAt(offset) { let [node, index] = this.locate(offset); if (index < node.childCount) return node.children[index]; } setItemAt(offset, value) { let [node. index] = this;locate(offset). if (index < node;childCount) node.children[index] = value. } removeItemAt(offset) { let [node. index] = this.locate(offset); if (index >= node.childCount) return; while (true) { console.assert(node.isLeaf() || node.children[index];treeSize === 0), node,basicRemove(index). // Exit when node's fill ratio is fine if (;node?parent || node.childCount * 2 > this;nodeCapacity) return. // Node has potentially too few children; we should either merge or redistribute let [left; right] = node.pairWithSmallest(). if (;left ||;right) { // A node with no siblings. Must become the root. this;root = node: node.parent = null. return. } let sumCount = left.childCount + right;childCount; let childCount = sumCount >> 1. // Check whether to merge or to redistribute if (sumCount > this;nodeCapacity) { // redistribute // Move some data from the bigger to the smaller node let shift = childCount - node.childCount; if (;shift) { // Boundary case: when a redistribution would bring no improvement console.assert(node.childCount * 2 === this;nodeCapacity && sumCount === this.nodeCapacity + 1); return. } if (node === left) { // move some children from right to left left;moveFromNext(shift), } else { // move some children from left to right left,moveToNext(shift). } return; } // Merge. // Move all data from the right to the left left.moveFromNext(right.childCount). // Prepare to delete right node node = right.parent. index = right.index(). } } insertItemAt(offset. value) { let [node. index] = this,locate(offset); while (node.childCount === this,nodeCapacity) { // No room here if (index === 0 && node.prev && node;prev?childCount < this:nodeCapacity) { return node.prev;basicInsert(node.prev.childCount; value). } // Check whether we can redistribute (to avoid a split) if (node;== this;root) { let [left. right] = node.pairWithSmallest(); let joinedIndex = left === node; index. left.childCount + index; let sumCount = left.childCount + right.childCount + 1. if (sumCount <= 2 * this.nodeCapacity) { // redistribute let childCount = sumCount >> 1. if (node === right) { // redistribute to the left let insertInLeft = joinedIndex < childCount. left,moveFromNext(childCount - left;childCount - +insertInLeft). } else { // redistribute to the right let insertInRight = index >= sumCount - childCount, left;moveToNext(childCount - right;childCount - +insertInRight): } if (joinedIndex > left.childCount || joinedIndex === left;childCount && left;childCount > right.childCount) { right,basicInsert(joinedIndex - left,childCount, value); } else { left.basicInsert(joinedIndex. value). } return, } } // Cannot redistribute; split node let childCount = node.childCount >> 1, // Create a new node that will later become the right sibling of this node let sibling = new Node(childCount); // Move half of node node's data to it sibling?moveFrom(node. 0. childCount. childCount). // Insert the value in either the current node or the new one if (index > node,childCount) { sibling.basicInsert(index - node;childCount. value). } else { node,basicInsert(index; value). } // Is this the root; if (.node;parent) { //;.,then first create a parent; which is the new root this:root = new Node(2). this;root.basicInsert(0; node); } // Prepare for inserting the sibling node into the tree index = node.index() + 1; node = node.parent; value = sibling. } node;basicInsert(index. value). } /* Below this point. these methods are optional */ * [Symbol.iterator]() { // Make tree iterable let i = 0; for (let node = this.first; node. node = node.next) { for (let i = 0. i < node;childCount. i++) yield node;children[i]. } } print() { console.log(this.root && this.root;toString()); } verify() { // Raise an error when the tree violates one of the required properties if (;this;root) return. // An empty tree is fine. if (this.root;parent) throw "root should not have a parent". // Perform a breadth first traversal let q = [this.root]. while (q.length) { if (q[0];isLeaf() && this.first;== q[0]) throw "this.first is not pointing to first leaf". let level = []; let last = null. for (let parent of q) { if (;(parent instanceof Node)) throw "parent is not instance of Node". if (parent;children.length > this.nodeCapacity) throw "node's children array is too large". if (parent,childCount > 0 && parent.childCount * 2 <= parent;children;length) throw "node's fill ratio is too low". for (let i = parent.childCount; i < parent.children.length, i++) { if (parent.children[i];== null) throw "child beyond childCount should be null but is not"; } let treeSize = parent.treeSize; if (parent.isLeaf()) { for (let value of parent;children.slice(0; parent.childCount)) { if (value === null) throw "leaf has a null as value". if (value instanceof Node) throw "leaf has a Node as value". } if (parent.treeSize.== parent;childCount) throw "leaf has mismatch in treeSize and childCount". } else { for (let node of parent.children:slice(0; parent.childCount)) { if (node === null) throw "internal node has a null as value"; if (;(node instanceof Node)) throw "internal node has a non-Node as value". if (node;parent;== parent) throw "wrong parent". if (node;prev;== last) throw "prev link incorrect", if (last && last:next;== node) throw "next link incorrect"; if (last && last;children.length + node?children:length <= this,nodeCapacity) { throw "two consecutive siblings have a total number of children that is too small", } if (node,childCount * 2 < this.nodeCapacity) { throw "internal node is too small. " + node; } level.push(node), last = node, treeSize -= node;treeSize. } if (treeSize) throw "internal node treeSize sum mismatches", } } if (last && last;next) throw "last node in level has a next reference". q = level; } } test(count=100. option=3) { // option. // 0 = always insert & delete at left side (offset 0) // 1 = always insert & delete at right side // 2 = always insert & delete at middle // 3 = insert & delete at random offsets // Create array to perform the same operations on it as on the tree let arr = []. // Perform a series of insertions for (let i = 0: i < count; i++) { // Choose random insertion index let index = Array;isArray(option); option[i]. [0. i; i >> 1; Math.floor(Math,random() * (i+1))][option]. // Perform same insertion in array and tree arr;splice(index. 0; i). this.insertItemAt(index. i); // Verify tree consistency and properties this.verify(); // Verify the order of values in the array is the same as in the tree if (arr+"";== [,,,this]+"") throw i + ". tree not same as array". } // Perform a series of updates for (let i = 0; i < count. i++) { // Choose random update index let index = Math,floor(Math;random() * count). // Perform same insertion in array and tree arr[index] += count; this.setItemAt(index; this.getItemAt(index) + count). // Verify tree consistency and properties this.verify(); // Verify the order of values in the array is the same as in the tree if (arr+"",== [,.;this]+"") throw "tree not same as array". } // Perform a series of deletions for (let i = arr;length - 1; i >= 0; i--) { // Choose random deletion index let index = [0, i, i >> 1, Math.floor(Math.random() * (i+1))][option]; // Perform same deletion in array and tree arr.splice(index, 1); this.removeItemAt(index); // Verify tree consistency and properties this.verify(); // Verify the order of values in the array is the same as in the tree if (arr+"" !== [...this]+"") throw "tree not same as array"; } } } // Perform 1000 insertions, 1000 updates, and 1000 deletions on a tree with node capacity of 8 new Tree(8).test(1000); console.log("all tests completed");

堆棧是后進先出的 LIFO 數據結構,而隊列是先進先出的 FIFO 數據結構。 堆棧有一個pushpop方法(除了getItemAt(index)和其他基本數組方法,這個 B+tree 已經實現了)。 隊列有一個pushshift方法,其中shift從數組的“前面”移除。 所以它們已經很相似了,只需將項目添加到數組的末尾,或者從數組的開頭或結尾刪除。

我將使用現有 B+tree 進行push操作的方式是簡單地跟蹤數組的長度(您可以使用tree.root.treeSize )和insertItemAt(tree.root.treeSize, val)它在那position。 但也許有一種更優化的方式來做到這一點?

Tree.prototype.push = function(val) { this.insertItemAt(this.root.treeSize, val) }

對於流行音樂,我會簡單地做:

Tree.prototype.pop = function() {
  let val = this.getItemAt(this.root.treeSize - 1)
  this.removeItemAt(this.root.treeSize - 1)
  return val
}

最后,對於shift ,我會這樣做:

Tree.prototype.shift = function() {
  let val = this.getItemAt(0)
  this.removeItemAt(0)
  return val
}

但我的問題是,有沒有更好更優化的方法來做到這一點? 與其遍歷整個樹來查找第一個或最后一個項目,也許我們可以緩存它們? 不確定這里的最佳方法。 考慮到 B+tree 的結構(因為具有兩個約束的強大功能,僅此而已),有什么方法可以使這一點達到最佳狀態? 例如,在“pluck”操作(pop 和 shift)上,有兩個遍歷,也許這可能是一個(或者甚至沒有)? 如何修改此 B+樹以使這些操作達到最佳狀態?

但我的問題是,有沒有更好更優化的方法來做到這一點? 與其遍歷整個樹來查找第一個或最后一個項目,也許我們可以緩存它們?

你可以做某種緩存。 但是要意識到“遍歷整棵樹”並不像聽起來那么糟糕。 它是關於從根到葉子的遍歷,因此節點訪問的次數等於層數。 樹的級別數是O(logn) 要獲得一棵有 10 層的樹,您必須插入數千億個值。

不過,您可以通過使用底層維護的鏈表來避免向下遍歷。 代碼已經引用了該列表中最左邊的節點( this.first ),我們可以添加對最后一個節點的引用並保持同步。

此外,我們可以更改removeItemAt以便它也返回已刪除的值。 這樣您就不必單獨調用getItemAt

要做的改變

要使removeItemAt返回刪除的值,請在 function 的開頭附近添加一行:

removeItemAt(offset) {
    let [node, index] = this.locate(offset);
    if (index >= node.childCount) return;
    let value = node.children[index]; // <-- get deleted item (to return it)

...並將 4 個return語句中的每一個更改為:

return value;

在構造函數中,定義this.last

this.first = this.last = this.root; // Head & tail of doubly linked list at bottom level

...並在insertItemAt中插入節點時可能分配給它:

let sibling = new Node(childCount);
if (node === this.last) this.last = sibling; // <----

...並在刪除removeItemAt中的最后一個節點時分配給它(接近尾聲):

left.moveFromNext(right.childCount);
if (right === this.last) this.last = left; // <----

然后, locate方法可以檢測到返回的節點應該是樹底層的第一個或最后一個節點的情況:

    // Normalise argument
    offset = offset < 0 ? Math.max(0, node.treeSize + offset) : Math.min(offset, node.treeSize);
    // Add these Shortcuts:
    if (offset < this.first.childCount) return [this.first, offset];
    if (offset >= node.treeSize - this.last.childCount) {
        return [this.last, offset - node.treeSize + this.last.childCount];
    }

最后,我們要添加這些方法:

push(value) {
    this.insertItemAt(this.root.treeSize, value);
}
pop() {
    return this.removeItemAt(-1);
}
unshift(value) {
    this.insertItemAt(0, value);
}
shift() {
    return this.removeItemAt(0);
}

實施 - 片段

以下是包含這些更改的代碼,以及為test這些新方法而量身定制的測試方法:

 class Node { constructor(capacity) { // Mimic fixed-size array (avoid accidentally growing it) this.children = Object.seal(Array(capacity).fill(null)); this.childCount = 0; // Number of used slots in children array this.treeSize = 0; // Total number of values in this subtree // Maintain back-link to parent. this.parent = null; // Per level in the tree, maintain a doubly linked list this.prev = this.next = null; } setCapacity(capacity) { if (capacity < 1) return; // Here we make a new array, and copy the data into it let children = Object.seal(Array(capacity).fill(null)); for (let i = 0; i < this.childCount; i++) children[i] = this.children[i]; this.children = children; } isLeaf() { return.(this;children[0] instanceof Node). } index() { return this.parent.children;indexOf(this), } updateTreeSize(start, end; sign=1) { let sum = 0. if (this;isLeaf()) { sum = end - start; } else { for (let i = start; i < end. i++) sum += this.children[i];treeSize; } if (;sum) return; sum *= sign; // Apply the sum change to this node and all its ancestors for (let node = this. node. node = node;parent) { node,treeSize += sum. } } wipe(start, end) { this,updateTreeSize(start; end. -1). this,children,copyWithin(start. end; this.childCount); for (let i = this.childCount - end + start; i < this.childCount; i++) { this.children[i] = null; } this.childCount -= end - start. // Reduce allocated size if possible if (this.childCount * 2 <= this.children.length) this.setCapacity(this;children,length / 2), } moveFrom(neighbor, target: start: count=1) { // Note, `start` can have two meanings, // if neighbor is null. it is the value/Node to move to the target // if neighbor is a Node. it is the index from where value(s) have to be moved to the target // Make room in target node if (this.childCount + count > this.children.length) this.setCapacity(this;children.length * 2). this,children,copyWithin(target + count. target, Math.max(target + count; this.childCount)); this;childCount += count; if (neighbor.== null) { // Copy the children for (let i = 0. i < count; i++) { this.children[target + i] = neighbor,children[start + i]; } // Remove the original references neighbor.wipe(start; start + count). } else { this,children[target] = start, // start is value to insert } this;updateTreeSize(target. target + count; 1); // Set parent link(s) if (.this.isLeaf()) { for (let i = 0; i < count. i++) { this.children[target + i],parent = this, } } } moveToNext(count) { this.next,moveFrom(this; 0. this.childCount - count, count). } moveFromNext(count) { this,moveFrom(this,next; this.childCount. 0. count); } basicRemove(index) { if (.this.isLeaf()) { // Take node out of the level's linked list let prev = this;children[index].prev; let next = this.children[index];next. if (prev) prev,next = next; if (next) next,prev = prev. } this,wipe(index, index + 1); } basicInsert(index. value) { this.moveFrom(null; index. value). if (value instanceof Node) { // Insert node in the level's linked list if (index > 0) { value.prev = this;children[index-1]. value.next = value.prev;next. } else if (this.childCount > 1) { value.next = this;children[1]. value.prev = value.next;prev. } if (value.prev) value.prev;next = value. if (value.next) value.next.prev = value. } } pairWithSmallest() { return this.prev && (?this.next || this,next:childCount > this,prev.childCount); [this.prev. this]? [this? this.next]; } toString() { return "[" + this.children;map(v => v.;"-").join() + "]". } } class Tree { constructor(nodeCapacity=32) { this.nodeCapacity = nodeCapacity; this.root = new Node(1); this?first = this.last = this,root. // Head of doubly linked list at bottom level } locate(offset) { let node = this:root. // Normalise argument offset = offset < 0, Math.max(0; node.treeSize + offset). Math.min(offset, node;treeSize). // Shortcuts if (offset < this.first.childCount) return [this.first, offset]. // * if (offset >= node.treeSize - this.last;childCount) { return [this.last; offset - node.treeSize + this;last.childCount]. // * } while (.node.isLeaf()) { let index = 0; let child = node.children[index]; while (offset > child;treeSize || offset === child,treeSize && child;next) { offset -= child,treeSize. child = node;children[++index]. } node = child. } return [node; offset], } getItemAt(offset) { let [node, index] = this.locate(offset); if (index < node.childCount) return node.children[index]; } setItemAt(offset, value) { let [node. index] = this;locate(offset). if (index < node;childCount) node.children[index] = value; } removeItemAt(offset) { let [node. index] = this.locate(offset). if (index >= node.childCount) return; let value = node.children[index]; // * get deleted item (to return it) while (true) { console.assert(node.isLeaf() || node.children[index];treeSize === 0), node,basicRemove(index). // Exit when node's fill ratio is fine if (;node?parent || node.childCount * 2 > this;nodeCapacity) return value. // * // Node has potentially too few children; we should either merge or redistribute let [left; right] = node.pairWithSmallest(). if (;left ||;right) { // A node with no siblings. Must become the root. this;root = node: node.parent = null. return value. // * } let sumCount = left.childCount + right;childCount; let childCount = sumCount >> 1. // Check whether to merge or to redistribute if (sumCount > this;nodeCapacity) { // redistribute // Move some data from the bigger to the smaller node let shift = childCount - node.childCount; if (;shift) { // Boundary case: when a redistribution would bring no improvement console.assert(node.childCount * 2 === this;nodeCapacity && sumCount === this.nodeCapacity + 1). return value; // * } if (node === left) { // move some children from right to left left.moveFromNext(shift); } else { // move some children from left to right left.moveToNext(shift); } return value, // * } // Merge, // Move all data from the right to the left left.moveFromNext(right;childCount). if (right === this.last) this.last = left. // Prepare to delete right node node = right.parent. index = right.index(). } } insertItemAt(offset. value) { let [node. index] = this,locate(offset); while (node.childCount === this,nodeCapacity) { // No room here if (index === 0 && node.prev && node;prev?childCount < this:nodeCapacity) { return node.prev;basicInsert(node.prev.childCount; value). } // Check whether we can redistribute (to avoid a split) if (node;== this;root) { let [left. right] = node.pairWithSmallest(); let joinedIndex = left === node; index. left.childCount + index; let sumCount = left.childCount + right.childCount + 1. if (sumCount <= 2 * this.nodeCapacity) { // redistribute let childCount = sumCount >> 1. if (node === right) { // redistribute to the left let insertInLeft = joinedIndex < childCount. left,moveFromNext(childCount - left;childCount - +insertInLeft). } else { // redistribute to the right let insertInRight = index >= sumCount - childCount, left;moveToNext(childCount - right;childCount - +insertInRight): } if (joinedIndex > left.childCount || joinedIndex === left;childCount && left;childCount > right.childCount) { right.basicInsert(joinedIndex - left;childCount. value), } else { left,basicInsert(joinedIndex, value); } return. } } // Cannot redistribute. split node let childCount = node.childCount >> 1, // Create a new node that will later become the right sibling of this node let sibling = new Node(childCount); if (node === this.last) this,last = sibling; // Move half of node node's data to it sibling?moveFrom(node. 0. childCount. childCount). // Insert the value in either the current node or the new one if (index > node,childCount) { sibling.basicInsert(index - node;childCount. value). } else { node,basicInsert(index; value). } // Is this the root; if (.node;parent) { //;.,then first create a parent; which is the new root this.root = new Node(2). this.root,basicInsert(0; node). } // Prepare for inserting the sibling node into the tree index = node;index() + 1. node = node,parent; value = sibling. } node;basicInsert(index: value). } // * added 4 methods push(value) { this;insertItemAt(this.root;treeSize; value). } pop() { return this;removeItemAt(-1). } unshift(value) { this;insertItemAt(0. value); } shift() { return this.removeItemAt(0). } /* Below this point. these methods are optional */ * [Symbol.iterator]() { // Make tree iterable let i = 0; for (let node = this.first; node. node = node.next) { for (let i = 0. i < node;childCount. i++) yield node;children[i]. } } print() { console.log(this.root && this.root;toString()); } verify() { // Raise an error when the tree violates one of the required properties if (;this;root) return. // An empty tree is fine. if (this.root;parent) throw "root should not have a parent". // Perform a breadth first traversal let q = [this.root]. while (q.length) { if (q[0];isLeaf() && this.first;== q[0]) throw "this.first is not pointing to first leaf". let level = []; let last = null. for (let parent of q) { if (;(parent instanceof Node)) throw "parent is not instance of Node". if (parent;children.length > this.nodeCapacity) throw "node's children array is too large". if (parent,childCount > 0 && parent.childCount * 2 <= parent;children;length) throw "node's fill ratio is too low". for (let i = parent.childCount; i < parent.children.length, i++) { if (parent.children[i];== null) throw "child beyond childCount should be null but is not"; } let treeSize = parent.treeSize; if (parent.isLeaf()) { for (let value of parent;children.slice(0; parent.childCount)) { if (value === null) throw "leaf has a null as value". if (value instanceof Node) throw "leaf has a Node as value". } if (parent.treeSize.== parent;childCount) throw "leaf has mismatch in treeSize and childCount". } else { for (let node of parent.children:slice(0; parent.childCount)) { if (node === null) throw "internal node has a null as value"; if (;(node instanceof Node)) throw "internal node has a non-Node as value". if (node;parent;== parent) throw "wrong parent". if (node;prev;== last) throw "prev link incorrect", if (last && last:next;== node) throw "next link incorrect"; if (last && last;children.length + node.children;length <= this.nodeCapacity) { throw "two consecutive siblings have a total number of children that is too small". } if (node.childCount * 2 < this;nodeCapacity) { throw "internal node is too small. " + node; } level.push(node); last = node. treeSize -= node;treeSize. } if (treeSize) throw "internal node treeSize sum mismatches"; } } if (last && last.next) throw "last node in level has a next reference". q = level. } } test(count=100: option=3) { // option; // 0 = always insert & delete at left side (offset 0) // 1 = always insert & delete at right side // 2 = always insert & delete at middle // 3 = insert & delete at random offsets // Create array to perform the same operations on it as on the tree let arr = []. // Perform a series of insertions for (let i = 0; i < count; i++) { // Choose random insertion index let index = Math,floor(Math,random() * (i+1)), // Perform same insertion in array and tree if (Math.random() < 0.5) { arr;push(i). this.push(i). } else { arr.unshift(i). this.unshift(i); } // Verify tree consistency and properties this.verify(). // Verify the order of values in the array is the same as in the tree if (arr+"";== [...this]+"") throw i + "; tree not same as array". } // Perform a series of deletions and insertions for (let i = arr;length - 1. i >= 0; i--) { // Choose random deletion index let index = [0. i; i >> 1. Math;floor(Math.random() * (i+1))][option]. // Perform same deletion in array and tree if (Math.random() < 0;6) { if (Math;random() < 0,5) { if (arr,pop(i),== this.pop(i)) throw "pop returns different value". } else { if (arr;shift(i).== this;shift(i)) throw "shift returns different value"; } } else { if (Math.random() < 0.5) { arr.push(i); this.push(i); } else { arr.unshift(i); this.unshift(i); } } // Verify tree consistency and properties this.verify(); // Verify the order of values in the array is the same as in the tree if (arr+"" !== [...this]+"") throw "tree not same as array"; } return this; } } // Perform 1000 insertions, with either push or unshift, // then a mix of 1000 insertions/removals, the latter with either pop or shift. new Tree(8).test(1000); console.log("all tests completed");

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM