Skip to content

Third edition #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 5, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/chapter10/01-UsingMinHeap.js
Original file line number Diff line number Diff line change
@@ -22,6 +22,6 @@ for (let i = 1; i < 10; i++) {

console.log(heap.getAsArray());

console.log('Extract minimum: ', heap.extract());
console.log('Extract minimum: ', heap.extract()); // 1
console.log(heap.getAsArray()); // [2, 4, 3, 8, 5, 6, 7, 9]

14 changes: 8 additions & 6 deletions src/ts/data-structures/models/red-black-node.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { Node } from './node';

export enum Colors {
RED = 0,
BLACK = 1
}

export class RedBlackNode<K> {
export class RedBlackNode<K> extends Node<K> {
left: RedBlackNode<K>;
right: RedBlackNode<K>;
parent: RedBlackNode<K>;
color: Colors;

constructor(public key: K) {}
constructor(public key: K) {
super(key);
this.color = Colors.RED;
}

isRed() {
return this.color === Colors.RED;
@@ -21,8 +27,4 @@ export class RedBlackNode<K> {
this.color = Colors.RED;
}
}

toString() {
return `${this.key}`;
}
}
175 changes: 172 additions & 3 deletions src/ts/data-structures/red-black-tree.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,180 @@
/*import { Compare, defaultCompare, ICompareFunction } from '../util';
import { defaultCompare, ICompareFunction, Compare } from '../util';
import BinarySearchTree from './binary-search-tree';
import { Node } from './models/node';
import { RedBlackNode, Colors } from './models/red-black-node';

export default class RedBlackTree<T> extends BinarySearchTree<T> {
protected root: RedBlackNode<T>;

constructor(protected compareFn: ICompareFunction<T> = defaultCompare) {
super(compareFn);
}

/**
* Left left case: rotate right
*
* b a
* / \ / \
* a e -> rotationLL(b) -> c b
* / \ / \
* c d d e
*
* @param node Node<T>
*/
private rotationLL(node: RedBlackNode<T>) {
const tmp = node.left;
node.left = tmp.right;
if (tmp.right && tmp.right.key) {
tmp.right.parent = node;
}
tmp.parent = node.parent;
if (!node.parent) {
this.root = tmp;
} else {
if (node === node.parent.left) {
node.parent.left = tmp;
} else {
node.parent.right = tmp;
}
}
tmp.right = node;
node.parent = tmp;
}

/**
* Right right case: rotate left
*
* a b
* / \ / \
* c b -> rotationRR(a) -> a e
* / \ / \
* d e c d
*
* @param node Node<T>
*/
private rotationRR(node: RedBlackNode<T>) {
const tmp = node.right;
node.right = tmp.left;
if (tmp.left && tmp.left.key) {
tmp.left.parent = node;
}
tmp.parent = node.parent;
if (!node.parent) {
this.root = tmp;
} else {
if (node === node.parent.left) {
node.parent.left = tmp;
} else {
node.parent.right = tmp;
}
}
tmp.left = node;
node.parent = tmp;
}

insert(key: T) {
// special case: first key
if (this.root == null) {
this.root = new RedBlackNode(key);
this.root.color = Colors.BLACK;
} else {
const newNode = this.insertNode(this.root, key);
this.fixTreeProperties(newNode);
}
}

protected insertNode(node: RedBlackNode<T>, key: T): RedBlackNode<T> {
if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
if (node.left == null) {
node.left = new RedBlackNode(key);
node.left.parent = node;
return node.left;
} else {
return this.insertNode(node.left, key);
}
} else if (node.right == null) {
node.right = new RedBlackNode(key);
node.right.parent = node;
return node.right;
} else {
return this.insertNode(node.right, key);
}
}

private fixTreeProperties(node: RedBlackNode<T>) {
while (node && node.parent && node.parent.color === Colors.RED && node.color !== Colors.BLACK) {
let parent = node.parent;
const grandParent = parent.parent;

// case A
if (grandParent && grandParent.left === parent) {

const uncle = grandParent.right;

// case 1: uncle of node is also red - only recoloring
if (uncle && uncle.color === Colors.RED) {
grandParent.color = Colors.RED;
parent.color = Colors.BLACK;
uncle.color = Colors.BLACK;
node = grandParent;
} else {
// case 2: node is right child - left rotate
if (node === parent.right) {
this.rotationRR(parent);
node = parent;
parent = node.parent;
}

// case 3: node is left child - right rotate
this.rotationLL(grandParent);
// swap color
parent.color = Colors.BLACK;
grandParent.color = Colors.RED;
node = parent;
}

} else { // case B: parent is right child of grand parent

const uncle = grandParent.left;

// case 1: uncle is read - only recoloring
if (uncle && uncle.color === Colors.RED) {
grandParent.color = Colors.RED;
parent.color = Colors.BLACK;
uncle.color = Colors.BLACK;
node = grandParent;
} else {
// case 2: node is left child - left rotate
if (node === parent.left) {
this.rotationLL(parent);
node = parent;
parent = node.parent;
}

// case 3: node is right child - left rotate
this.rotationRR(grandParent);
// swap color
parent.color = Colors.BLACK;
grandParent.color = Colors.RED;
node = parent;
}
}
}
this.root.color = Colors.BLACK;
}

getRoot() {
return this.root;
}

/* private flipColors(node: RedBlackNode<T>) {
node.left.flipColor();
node.right.flipColor();
}
private isRed(node: RedBlackNode<T>) {
if (!node) {
return false;
}
return node.isRed();
}*/
}
*/
1 change: 1 addition & 0 deletions src/ts/index.ts
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ export { fibonacciMemoization as fibonacciMemoization} from './others/fibonacci'
// chapter 09
export { default as BinarySearchTree } from './data-structures/binary-search-tree';
export { default as AVLTree } from './data-structures/avl-tree';
export { default as RedBlackTree } from './data-structures/red-black-tree';

// chapter 10
export { MinHeap as MinHeap } from './data-structures/heap';
112 changes: 112 additions & 0 deletions test/ts/data-structures/red-black-tree.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Colors } from './../../../src/ts/data-structures/models/red-black-node';
import 'mocha';
import { expect } from 'chai';
import { RedBlackTree } from '../../../src/ts/index';

describe('RedBlackTree', () => {
let tree: RedBlackTree<number>;

beforeEach(() => {
tree = new RedBlackTree<number>();
});

it('starts empty', () => {
expect(tree.getRoot()).to.equal(undefined);
});

it('inserts elements in the RedBlackTree', () => {
expect(tree.getRoot()).to.equal(undefined);

let node;

tree.insert(1);
assertNode(tree.getRoot(), 1, Colors.BLACK);

tree.insert(2);
assertNode(tree.getRoot(), 1, Colors.BLACK);
assertNode(tree.getRoot().right, 2, Colors.RED);

tree.insert(3);
assertNode(tree.getRoot(), 2, Colors.BLACK);
assertNode(tree.getRoot().right, 3, Colors.RED);
assertNode(tree.getRoot().left, 1, Colors.RED);

tree.insert(4);
assertNode(tree.getRoot(), 2, Colors.BLACK);
assertNode(tree.getRoot().left, 1, Colors.BLACK);
assertNode(tree.getRoot().right, 3, Colors.BLACK);
assertNode(tree.getRoot().right.right, 4, Colors.RED);

tree.insert(5);
assertNode(tree.getRoot(), 2, Colors.BLACK);
assertNode(tree.getRoot().left, 1, Colors.BLACK);
node = tree.getRoot().right;
assertNode(node, 4, Colors.BLACK);
assertNode(node.left, 3, Colors.RED);
assertNode(node.right, 5, Colors.RED);

tree.insert(6);
assertNode(tree.getRoot(), 2, Colors.BLACK);
assertNode(tree.getRoot().left, 1, Colors.BLACK);
node = tree.getRoot().right;
assertNode(node, 4, Colors.RED);
assertNode(node.left, 3, Colors.BLACK);
assertNode(node.right, 5, Colors.BLACK);
assertNode(node.right.right, 6, Colors.RED);

tree.insert(7);
assertNode(tree.getRoot(), 2, Colors.BLACK);
assertNode(tree.getRoot().left, 1, Colors.BLACK);
node = tree.getRoot().right;
assertNode(node, 4, Colors.RED);
assertNode(node.left, 3, Colors.BLACK);
assertNode(node.right, 6, Colors.BLACK);
assertNode(node.right.right, 7, Colors.RED);
assertNode(node.right.left, 5, Colors.RED);

tree.insert(8);
assertNode(tree.getRoot(), 4, Colors.BLACK);
node = tree.getRoot().left;
assertNode(node, 2, Colors.RED);
assertNode(node.left, 1, Colors.BLACK);
assertNode(node.right, 3, Colors.BLACK);
node = tree.getRoot().right;
assertNode(node, 6, Colors.RED);
assertNode(node.left, 5, Colors.BLACK);
assertNode(node.right, 7, Colors.BLACK);
assertNode(node.right.right, 8, Colors.RED);

tree.insert(9);
assertNode(tree.getRoot(), 4, Colors.BLACK);
node = tree.getRoot().left;
assertNode(node, 2, Colors.RED);
assertNode(node.left, 1, Colors.BLACK);
assertNode(node.right, 3, Colors.BLACK);
node = tree.getRoot().right;
assertNode(node, 6, Colors.RED);
assertNode(node.left, 5, Colors.BLACK);
assertNode(node.right, 8, Colors.BLACK);
assertNode(node.right.left, 7, Colors.RED);
assertNode(node.right.right, 9, Colors.RED);

tree.insert(10);
assertNode(tree.getRoot(), 4, Colors.BLACK);
node = tree.getRoot().left;
assertNode(node, 2, Colors.BLACK);
assertNode(node.left, 1, Colors.BLACK);
assertNode(node.right, 3, Colors.BLACK);
node = tree.getRoot().right;
assertNode(node, 6, Colors.BLACK);
assertNode(node.left, 5, Colors.BLACK);
assertNode(node.right, 8, Colors.RED);
assertNode(node.right.left, 7, Colors.BLACK);
assertNode(node.right.right, 9, Colors.BLACK);
assertNode(node.right.right.right, 10, Colors.RED);

});

function assertNode(node, key, color) {
expect(node.color).to.equal(color);
expect(node.key).to.equal(key);
}
});