diff --git a/README.md b/README.md
index 173c0ad..3af1689 100644
--- a/README.md
+++ b/README.md
@@ -50,6 +50,7 @@ Trigger production build
Data structures covered so far -
- [Binary Search Tree](#binary-search-tree)
+- [AVL Tree](#avl-tree) - In Progress
- [Graph](#graph)
- [Queue](#queue)
- [Linked List](#link-list)
@@ -130,6 +131,8 @@ Delete elements from binary search tree
bst.delete(10);
bst.delete(20);
```
+# AVL Tree
+AVL Tree Docs.
# Graph
diff --git a/package.json b/package.json
index 437d2f0..2eb053d 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"prepare": "npm run build & npm run dev",
"publish:npm": "npm publish --access public",
"test:jest": "jest",
+ "test:jest:watch": "jest --watch",
"lint": "eslint src/ --ext .ts"
},
"jest": {
@@ -44,7 +45,8 @@
"es6",
"queue",
"graph",
- "binary search tree"
+ "binary search tree",
+ "avl tree"
],
"author": {
"name": "Abhishek Prakash",
diff --git a/src/avl-tree/__test__/avl-node.spec.ts b/src/avl-tree/__test__/avl-node.spec.ts
new file mode 100644
index 0000000..e1bb12d
--- /dev/null
+++ b/src/avl-tree/__test__/avl-node.spec.ts
@@ -0,0 +1,59 @@
+import { AVLNode } from "../avl-node";
+
+class Int extends AVLNode {
+ constructor(value: number) {
+ super(value)
+ }
+
+ public compareTo(o: AVLNode): number {
+ if (this.getValue() < o.getValue()) {
+ return -1;
+ } else if (this.getValue() > o.getValue()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+describe("Test for AVLNode", () => {
+ let node: AVLNode;
+ let node1: AVLNode;
+ let node2: AVLNode;
+ let node3: AVLNode;
+
+ beforeEach(() => {
+ node = new Int(5);
+ node1 = new Int(4);
+ node2 = new Int(5);
+ node3 = new Int(6);
+ });
+
+ it("should create an AVL node of type number", () => {
+ expect(node).toBeDefined();
+ expect(node.getValue()).toBe(5);
+ expect(node.getLeft()).toBe(null);
+ expect(node.getRight()).toBe(null);
+ expect(node.getLeftHeight()).toBe(0);
+ expect(node.getRightHeight()).toBe(0);
+ expect(node.nodeHeight()).toBe(0);
+ expect(node.balanceFactor()).toBe(0);
+ expect(node.compareTo(node1)).toBe(1);
+ expect(node.compareTo(node2)).toBe(0);
+ expect(node.compareTo(node3)).toBe(-1);
+ });
+
+ it("should calculate the hight correctly", () => {
+ node.setLeft(node1);
+ node.setRight(node3);
+ node.setLeftHeight(1);
+ node.setRightHeight(1)
+
+ expect(node.getLeft()).toEqual(node1);
+ expect(node.getRight()).toEqual(node3);
+ expect(node.getLeftHeight()).toBe(1);
+ expect(node.getRightHeight()).toBe(1);
+ expect(node.nodeHeight()).toBe(1);
+ expect(node.balanceFactor()).toBe(0);
+ })
+});
\ No newline at end of file
diff --git a/src/avl-tree/__test__/avl.spec.ts b/src/avl-tree/__test__/avl.spec.ts
new file mode 100644
index 0000000..67e0da3
--- /dev/null
+++ b/src/avl-tree/__test__/avl.spec.ts
@@ -0,0 +1,58 @@
+import { AVLTree } from "../avl"
+import { AVLNode } from "../avl-node";
+
+class Int extends AVLNode {
+ constructor(value: number) {
+ super(value)
+ }
+
+ public compareTo(o: AVLNode): number {
+ if (this.getValue() < o.getValue()) {
+ return -1;
+ } else if (this.getValue() > o.getValue()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+describe("AVL Tree", () => {
+ describe("Insert operation", () => {
+ const avlTree = new AVLTree();
+
+ it("should have null node", () => {
+ const root = avlTree.getRoot();
+ expect(root).toBe(null);
+ });
+
+ it("should add a node as root node in an empty tree", () => {
+ const isNodeInsterted = avlTree.insert(new Int(5));
+ const root = avlTree.getRoot();
+ expect(isNodeInsterted).toBeTruthy();
+ expect(root.getValue()).toBe(5);
+ expect(root.nodeHeight()).toBe(0);
+ });
+
+ it("should add a left child to the avl tree", () => {
+ const isNodeInsterted = avlTree.insert(new Int(3));
+ const left = avlTree.getRoot().getLeft();
+ expect(isNodeInsterted).toBeTruthy();
+ expect(left.getValue()).toBe(3);
+ expect(avlTree.getRoot().nodeHeight()).toBe(1);
+ });
+
+ it("should add a right child to the avl tree", () => {
+ const isNodeInsterted = avlTree.insert(new Int(9));
+ const right = avlTree.getRoot().getRight();
+ expect(isNodeInsterted).toBeTruthy();
+ expect(right.getValue()).toBe(9);
+ expect(avlTree.getRoot().nodeHeight()).toBe(1);
+ });
+
+ it("should not add node with the same value", () => {
+ const isNodeInsterted = avlTree.insert(new Int(9));
+ expect(isNodeInsterted).toBeFalsy();
+ })
+ })
+})
\ No newline at end of file
diff --git a/src/avl-tree/avl-node.ts b/src/avl-tree/avl-node.ts
new file mode 100644
index 0000000..41c4e36
--- /dev/null
+++ b/src/avl-tree/avl-node.ts
@@ -0,0 +1,89 @@
+export abstract class AVLNode {
+ private left: AVLNode;
+ private right: AVLNode;
+ private leftHeight: number;
+ private rightHeight: number;
+ private value: number;
+ private parent: AVLNode
+
+
+ constructor(value: number) {
+ this.left = null;
+ this.right = null;
+ this.parent = null;
+ this.leftHeight = 0;
+ this.rightHeight = 0;
+ this.value = value;
+ }
+
+ public balanceFactor(): number {
+ return (this.rightHeight - this.leftHeight);
+ }
+
+ public nodeHeight(): number {
+ return (this.rightHeight > this.leftHeight) ? this.rightHeight : this.leftHeight;
+ }
+
+ public setNodeHeights(): void {
+ if (this.right == null) {
+ this.rightHeight = 0;
+ } else {
+ this.rightHeight = this.right.nodeHeight() + 1;
+ }
+
+ if (this.left == null) {
+ this.leftHeight = 0;
+ } else {
+ this.leftHeight = this.left.nodeHeight() + 1;
+ }
+ }
+
+ public abstract compareTo(o: AVLNode): number
+
+ /* #region(collapsed) Getter and Setter */
+ public getLeft(): AVLNode {
+ return this.left;
+ }
+
+ public setLeft(left: AVLNode): void {
+ this.left = left;
+ }
+
+ public getRight(): AVLNode {
+ return this.right;
+ }
+
+ public setRight(right: AVLNode): void {
+ this.right = right;
+ }
+
+ public getLeftHeight(): number {
+ return this.leftHeight;
+ }
+
+ public setLeftHeight(leftHeight: number): void {
+ this.leftHeight = leftHeight;
+ }
+
+ public getRightHeight(): number {
+ return this.rightHeight;
+ }
+
+ public setRightHeight(rightHeight: number): void {
+ this.rightHeight = rightHeight;
+ }
+
+ public getValue(): number {
+ return this.value;
+ }
+ public getParent(): AVLNode {
+ return this.parent;
+ }
+
+ public setParent(parent: AVLNode): void {
+ this.parent = parent;
+ }
+
+ /* #endregion */
+
+}
\ No newline at end of file
diff --git a/src/avl-tree/avl.ts b/src/avl-tree/avl.ts
new file mode 100644
index 0000000..684a000
--- /dev/null
+++ b/src/avl-tree/avl.ts
@@ -0,0 +1,72 @@
+import { AVLNode } from "./avl-node";
+
+export interface IAVLTree {
+ insert(node: AVLNode): boolean
+ search(node: AVLNode): boolean
+ delete(node: AVLNode): boolean
+}
+
+export class AVLTree implements IAVLTree {
+ constructor(private root: AVLNode = null) { }
+
+ public insert(node: AVLNode, root: AVLNode = this.root): boolean {
+ if (root == null) {
+ this.root = node;
+ return true;
+ }
+
+ if (node.compareTo(root) === 0) {
+ return false;
+ } else if (node.compareTo(root) > 0) {
+ if (root.getRight() == null) {
+ root.setRight(node);
+ this.updateNodeAfterInsert(node, root);
+ return true;
+ } else {
+ return this.insert(node, root.getRight());
+ }
+ } else {
+ if (root.getLeft() == null) {
+ root.setLeft(node);
+ this.updateNodeAfterInsert(node, root);
+ return true;
+ } else {
+ return this.insert(node, root.getLeft());
+ }
+ }
+ }
+
+ public search(node: AVLNode): boolean {
+ // TODO: Implement search
+ return true;
+ }
+ public delete(node: AVLNode): boolean {
+ // TODO: Implement delete
+ return true;
+ }
+
+ public getRoot(): AVLNode {
+ return this.root;
+ }
+
+ private updateNodeAfterInsert(node: AVLNode, parent: AVLNode): void {
+ node.setParent(parent);
+ node.setNodeHeights();
+ parent.setNodeHeights()
+ }
+ private detectRotation(node: AVLNode): void {
+ // TODO: add logic
+ }
+ private leftRotation(node: AVLNode): void {
+ // TODO: add logic
+ }
+ private rightRotation(node: AVLNode): void {
+ // TODO: add logic
+ }
+ private leftRightRotation(node: AVLNode): void {
+ // TODO: add logic
+ }
+ private rightLeftRotation(node: AVLNode): void {
+ // TODO: add logic
+ }
+}
\ No newline at end of file
diff --git a/src/binary-search-tree/avl-tree.js b/src/binary-search-tree/avl-tree.js
new file mode 100644
index 0000000..1061881
--- /dev/null
+++ b/src/binary-search-tree/avl-tree.js
@@ -0,0 +1,78 @@
+import { BST, BSTNode } from './binary-search-tree';
+
+export class AVLNode extends BSTNode {
+ constructor(key, details = null, left = null, right = null, height = 0) {
+ super(key, details, left, right);
+ this._height = height;
+ }
+
+ get height() { return this._height; }
+ set height(h) { this._height = h; }
+}
+
+export const ROTATION = {
+ LEFT_LEFT: 'll',
+ LEFT_RIGHT: 'lr',
+ RIGHT_RIGHT: 'rr',
+ RIGHT_LEFT: 'rl'
+};
+
+export class AVLTree extends BST {
+ insert(el, root = this.root) {
+
+ if (this.root === null) {
+ const node = new AVLNode(el);
+ this.root = node;
+ this.len = ++this.len;
+ return;
+ }
+
+ if (root === null) {
+ this.len = ++this.len;
+ return new AVLNode(el);
+ }
+
+ if (el < root.key) {
+ root.left = this.insert(el, root.left);
+ } else if (el > root.key) {
+ root.right = this.insert(el, root.right);
+ } else {
+ return root;
+ }
+
+ root.height = this.height(root);
+ const balanceFactor = this.getBalanceState(root);
+
+ if (balanceFactor > 1 || balanceFactor < -1) {
+ this.rebalance(node);
+ }
+ }
+
+ rebalance(node) {
+ if (node.left.height - node.right.height > 1) {
+ if (this.height(node.left.left) > this.height(node.left.right)) {
+ this.rightRotate(node);
+ } else {
+ this.leftRightRotate(node);
+ }
+ } else {
+ if (this.height(node.right.left) > this.height(node.right.right)) {
+ this.leftRotate(node);
+ } else {
+ this.rightLeftRotate(node);
+ }
+ }
+ }
+
+ leftRotate(node) { }
+ rightRotate(node) { }
+ leftRightRotate(node) { }
+ rightLeftRotate(node) { }
+
+ getBalanceState(node) {
+ const leftHeight = node.left ? node.left.height : 0;
+ const rightHeight = node.right ? node.right.height : 0;
+
+ return leftHeight - rightHeight;
+ }
+}
\ No newline at end of file
diff --git a/src/binary-search-tree/avl-tree.spec.js b/src/binary-search-tree/avl-tree.spec.js
new file mode 100644
index 0000000..75bf523
--- /dev/null
+++ b/src/binary-search-tree/avl-tree.spec.js
@@ -0,0 +1,34 @@
+import { AVLTree } from './avl-tree';
+
+describe('AVL Tree', () => {
+ let avlTree;
+ beforeEach(() => {
+ avlTree = new AVLTree;
+ });
+
+ it('should create an avl tree', () => {
+ expect(avlTree).toBeDefined();
+ expect(avlTree.len).toBe(0);
+ expect(avlTree.root).toBe(null);
+ });
+
+ it('should insert nodes in the tree', () => {
+ avlTree.insert(5);
+ expect(avlTree.len).toBe(1);
+ expect(avlTree.root.key).toBe(5);
+ expect(avlTree.root.height).toBe(0);
+
+ avlTree.insert(9);
+ expect(avlTree.len).toBe(2);
+ expect(avlTree.root.right.key).toBe(9);
+ expect(avlTree.root.right.height).toBe(0);
+ expect(avlTree.root.height).toBe(1);
+
+ avlTree.insert(9);
+ expect(avlTree.len).toBe(2);
+ expect(avlTree.root.right.key).toBe(9);
+ expect(avlTree.root.right.height).toBe(0);
+ expect(avlTree.root.height).toBe(1);
+ });
+});
+
diff --git a/src/binary-search-tree/binary-search-tree.ts b/src/binary-search-tree/binary-search-tree.ts
index d93e2f6..f26b4fe 100644
--- a/src/binary-search-tree/binary-search-tree.ts
+++ b/src/binary-search-tree/binary-search-tree.ts
@@ -67,10 +67,7 @@ const preOrderTraversal = Symbol("preorder");
const postOrderTraversal = Symbol("postorder");
const levelOrderTraversal = Symbol("levelorder");
-/**
- * Private properties name
- */
-const length = Symbol("length");
+
/**
* Binary Search Tree
@@ -83,8 +80,10 @@ export class BST {
this.root = null;
this.length = 0;
}
-
- get len() {
+ set len(l) {
+ this.length = l;
+ }
+ get len(): number {
return this.length;
}