Skip to content

Commit

Permalink
Simplify AVL tree node deletion.
Browse files Browse the repository at this point in the history
  • Loading branch information
trekhleb committed Sep 24, 2018
1 parent 04e533e commit afa4948
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 87 deletions.
61 changes: 5 additions & 56 deletions src/data-structures/tree/avl-tree/AvlTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,54 +16,16 @@ export default class AvlTree extends BinarySearchTree {
}
}

remove(value) {
const nodeToRemove = this.root.find(value);

if (!nodeToRemove) {
throw new Error('Item not found in the tree');
}

// Recursively find target node, if found then delete and balance.
// return nodeToRemove.value;
this.root = this.removeRecv(this.root, nodeToRemove);
}

removeRecv(origin, node) {
let newOrigin = origin;
if (origin.value > node.value) {
// Recursively traversing from left
newOrigin.left = this.removeRecv(origin.left, node);
} else if (origin.value < node.value) {
// Recursively traversing from right
newOrigin.right = this.removeRecv(origin.right, node);
} else {
if (origin.left == null) {
// Forget right node
return origin.right;
}
if (origin.right == null) {
// Forget left node
return origin.left;
}

// Recursively find min node from left subtree
// more efficient traversing
const parent = Object.assign({}, origin);
newOrigin = parent.right.findMin();
newOrigin.right = this.deleteMin(parent.right);
newOrigin.left = parent.left;
}

// Balance and return root node
return this.balance(newOrigin);
}

/**
* @param {*} value
* @return {boolean}
*/
remove(value) {
throw new Error(`Can't remove ${value}. Remove method is not implemented yet`);
// Do standard BST removal.
super.remove(value);

// Balance the tree starting from the root node.
this.balance(this.root);
}

/**
Expand All @@ -90,8 +52,6 @@ export default class AvlTree extends BinarySearchTree {
this.rotateRightLeft(node);
}
}
// Return the heap to avoid referenceError
return node;
}

/**
Expand Down Expand Up @@ -200,15 +160,4 @@ export default class AvlTree extends BinarySearchTree {
// Attach rootNode to the left of rightNode.
rightNode.setLeft(rootNode);
}

deleteMin(node) {
// Forget right node if has value
if (node.left == null) return node.right;

const newNode = node;
newNode.left = this.deleteMin(node.left);

// Balance and return root node
return this.balance(newNode);
}
}
98 changes: 67 additions & 31 deletions src/data-structures/tree/avl-tree/__test__/AvlTRee.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,31 +186,6 @@ describe('AvlTree', () => {
expect(tree.toString()).toBe('6,8,9,18,21,22,43');
});

it('should keep balance after removal', () => {
const tree = new AvlTree();

tree.insert(1);
tree.insert(2);
tree.insert(3);
tree.insert(4);
tree.insert(5);
tree.insert(6);
tree.insert(7);
tree.insert(8);
tree.insert(9);

expect(tree.toString()).toBe('1,2,3,4,5,6,7,8,9');
expect(tree.root.height).toBe(3);

tree.remove(8);
tree.remove(9);

expect(tree.contains(8)).toBeFalsy();
expect(tree.contains(9)).toBeFalsy();
expect(tree.toString()).toBe('1,2,3,4,5,6,7');
expect(tree.root.height).toBe(2);
});

it('should do left right rotation and keeping left right node safe', () => {
const tree = new AvlTree();

Expand Down Expand Up @@ -255,13 +230,74 @@ describe('AvlTree', () => {
expect(tree.root.height).toBe(3);
});

it('should throw an error when trying to remove the node', () => {
const removeNodeAvlTree = () => {
const tree = new AvlTree();
it('should remove values from the tree with right-right rotation', () => {
const tree = new AvlTree();

tree.insert(10);
tree.insert(20);
tree.insert(30);
tree.insert(40);

expect(tree.toString()).toBe('10,20,30,40');

tree.remove(10);

expect(tree.toString()).toBe('20,30,40');
expect(tree.root.value).toBe(30);
expect(tree.root.left.value).toBe(20);
expect(tree.root.right.value).toBe(40);
expect(tree.root.balanceFactor).toBe(0);
});

it('should remove values from the tree with left-left rotation', () => {
const tree = new AvlTree();

tree.insert(10);
tree.insert(20);
tree.insert(30);
tree.insert(5);

expect(tree.toString()).toBe('5,10,20,30');

tree.remove(30);

expect(tree.toString()).toBe('5,10,20');
expect(tree.root.value).toBe(10);
expect(tree.root.left.value).toBe(5);
expect(tree.root.right.value).toBe(20);
expect(tree.root.balanceFactor).toBe(0);
});

it('should keep balance after removal', () => {
const tree = new AvlTree();

tree.insert(1);
tree.insert(2);
tree.insert(3);
tree.insert(4);
tree.insert(5);
tree.insert(6);
tree.insert(7);
tree.insert(8);
tree.insert(9);

expect(tree.toString()).toBe('1,2,3,4,5,6,7,8,9');
expect(tree.root.value).toBe(4);
expect(tree.root.height).toBe(3);
expect(tree.root.balanceFactor).toBe(-1);

tree.remove(8);

tree.remove(1);
};
expect(tree.root.value).toBe(4);
expect(tree.root.balanceFactor).toBe(-1);

tree.remove(9);

expect(removeNodeAvlTree).toThrowError();
expect(tree.contains(8)).toBeFalsy();
expect(tree.contains(9)).toBeFalsy();
expect(tree.toString()).toBe('1,2,3,4,5,6,7');
expect(tree.root.value).toBe(4);
expect(tree.root.height).toBe(2);
expect(tree.root.balanceFactor).toBe(0);
});
});

0 comments on commit afa4948

Please sign in to comment.