Skip to content

Commit 8d07bc9

Browse files
committed
[Trees]
1 parent a1bea0b commit 8d07bc9

File tree

4 files changed

+303
-9
lines changed

4 files changed

+303
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1+
import { Node } from './node';
2+
13
export enum Colors {
24
RED = 0,
35
BLACK = 1
46
}
57

6-
export class RedBlackNode<K> {
8+
export class RedBlackNode<K> extends Node<K> {
79
left: RedBlackNode<K>;
810
right: RedBlackNode<K>;
11+
parent: RedBlackNode<K>;
912
color: Colors;
1013

11-
constructor(public key: K) {}
14+
constructor(public key: K) {
15+
super(key);
16+
this.color = Colors.RED;
17+
}
1218

1319
isRed() {
1420
return this.color === Colors.RED;
@@ -21,8 +27,4 @@ export class RedBlackNode<K> {
2127
this.color = Colors.RED;
2228
}
2329
}
24-
25-
toString() {
26-
return `${this.key}`;
27-
}
2830
}

src/ts/data-structures/red-black-tree.ts

+208-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,216 @@
1-
/*import { Compare, defaultCompare, ICompareFunction } from '../util';
1+
import { defaultCompare, ICompareFunction, Compare } from '../util';
22
import BinarySearchTree from './binary-search-tree';
3-
import { Node } from './models/node';
3+
import { RedBlackNode, Colors } from './models/red-black-node';
44

55
export default class RedBlackTree<T> extends BinarySearchTree<T> {
6+
protected root: RedBlackNode<T>;
67

78
constructor(protected compareFn: ICompareFunction<T> = defaultCompare) {
89
super(compareFn);
910
}
11+
12+
/**
13+
* Left left case: rotate right
14+
*
15+
* b a
16+
* / \ / \
17+
* a e -> rotationLL(b) -> c b
18+
* / \ / \
19+
* c d d e
20+
*
21+
* @param node Node<T>
22+
*/
23+
private rotationLL(node: RedBlackNode<T>) {
24+
const tmp = node.left;
25+
node.left = tmp.right;
26+
if (typeof tmp.right.key !== 'undefined') {
27+
tmp.right.parent = node;
28+
}
29+
tmp.parent = node.parent;
30+
if (!node.parent) {
31+
this.root = tmp;
32+
} else {
33+
if (node === node.parent.left) {
34+
node.parent.left = tmp;
35+
} else {
36+
node.parent.right = tmp;
37+
}
38+
}
39+
tmp.right = node;
40+
node.parent = tmp;
41+
}
42+
43+
/**
44+
* Right right case: rotate left
45+
*
46+
* a b
47+
* / \ / \
48+
* c b -> rotationRR(a) -> a e
49+
* / \ / \
50+
* d e c d
51+
*
52+
* @param node Node<T>
53+
*/
54+
private rotationRR(node: RedBlackNode<T>) {
55+
const tmp = node.right;
56+
node.right = tmp.left;
57+
if (typeof tmp.left.key !== 'undefined') {
58+
tmp.left.parent = node;
59+
}
60+
tmp.parent = node.parent;
61+
if (!node.parent) {
62+
this.root = tmp;
63+
} else {
64+
if (node === node.parent.left) {
65+
node.parent.left = tmp;
66+
} else {
67+
node.parent.right = tmp;
68+
}
69+
}
70+
tmp.left = node;
71+
node.parent = tmp;
72+
}
73+
74+
insert(key: T) {
75+
// special case: first key
76+
if (this.root == null) {
77+
this.root = new RedBlackNode(key);
78+
this.root.color = Colors.BLACK;
79+
} else {
80+
const newNode = this.insertNode(this.root, key);
81+
this.fixTreeProperties(newNode);
82+
}
83+
}
84+
85+
protected insertNode(node: RedBlackNode<T>, key: T): RedBlackNode<T> {
86+
if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
87+
if (node.left == null) {
88+
node.left = new RedBlackNode(key);
89+
node.left.parent = node;
90+
return node.left;
91+
} else {
92+
return this.insertNode(node.left, key);
93+
}
94+
} else if (node.right == null) {
95+
node.right = new RedBlackNode(key);
96+
node.right.parent = node;
97+
return node.right;
98+
} else {
99+
return this.insertNode(node.right, key);
100+
}
101+
}
102+
103+
private fixTreeProperties(node: RedBlackNode<T>) {
104+
let uncle: RedBlackNode<T>;
105+
let grandParent: RedBlackNode<T>;
106+
while (node && node.parent && node.parent.color === Colors.RED && node.color !== Colors.BLACK) {
107+
grandParent = node.parent.parent;
108+
if (grandParent && grandParent.left === node.parent) {
109+
if (grandParent.right != null) {
110+
uncle = grandParent.right;
111+
if (uncle.color === Colors.RED) {
112+
node.parent.color = Colors.BLACK;
113+
uncle.color = Colors.BLACK;
114+
grandParent.color = Colors.RED;
115+
node = grandParent;
116+
}
117+
} else {
118+
if (node.parent.right === node) {
119+
node = node.parent;
120+
this.leftRotate(node);
121+
}
122+
node.parent.color = Colors.BLACK;
123+
grandParent.color = Colors.RED;
124+
this.rightRotate(grandParent);
125+
}
126+
} else {
127+
if (grandParent.left != null) {
128+
uncle = grandParent.left;
129+
if (uncle.color === Colors.RED) {
130+
node.parent.color = Colors.BLACK;
131+
uncle.color = Colors.BLACK;
132+
grandParent.color = Colors.RED;
133+
node = grandParent;
134+
}
135+
} else {
136+
if (node.parent.left === node) {
137+
node = node.parent;
138+
this.rightRotate(node);
139+
}
140+
node.parent.color = Colors.BLACK;
141+
grandParent.color = Colors.RED;
142+
this.leftRotate(grandParent);
143+
}
144+
}
145+
}
146+
this.root.color = Colors.BLACK;
147+
}
148+
149+
leftRotate(p: RedBlackNode<T>) {
150+
if (p.right != null) {
151+
const y = p.right;
152+
if (y.left != null) {
153+
p.right = y.left;
154+
y.left.parent = p;
155+
} else {
156+
p.right = null;
157+
}
158+
if (p.parent != null) {
159+
y.parent = p.parent;
160+
}
161+
if (p.parent == null) {
162+
this.root = y;
163+
} else {
164+
if (p === p.parent.left) {
165+
p.parent.left = y;
166+
} else {
167+
p.parent.right = y;
168+
}
169+
}
170+
y.left = p;
171+
p.parent = y;
172+
}
173+
}
174+
175+
rightRotate(p: RedBlackNode<T>) {
176+
if (p.left != null) {
177+
const y = p.left;
178+
if (y.right != null) {
179+
p.left = y.right;
180+
y.right.parent = p;
181+
} else {
182+
p.left = null;
183+
}
184+
if (p.parent != null) {
185+
y.parent = p.parent;
186+
}
187+
if (p.parent == null) {
188+
this.root = y;
189+
} else {
190+
if (p === p.parent.left) {
191+
p.parent.left = y;
192+
} else {
193+
p.parent.right = y;
194+
}
195+
}
196+
y.right = p;
197+
p.parent = y;
198+
}
199+
}
200+
201+
getRoot() {
202+
return this.root;
203+
}
204+
205+
/* private flipColors(node: RedBlackNode<T>) {
206+
node.left.flipColor();
207+
node.right.flipColor();
208+
}
209+
210+
private isRed(node: RedBlackNode<T>) {
211+
if (!node) {
212+
return false;
213+
}
214+
return node.isRed();
215+
}*/
10216
}
11-
*/

src/ts/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export { fibonacciMemoization as fibonacciMemoization} from './others/fibonacci'
2323
// chapter 09
2424
export { default as BinarySearchTree } from './data-structures/binary-search-tree';
2525
export { default as AVLTree } from './data-structures/avl-tree';
26+
export { default as RedBlackTree } from './data-structures/red-black-tree';
2627

2728
// chapter 10
2829
export { MinHeap as MinHeap } from './data-structures/heap';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Colors } from './../../../src/ts/data-structures/models/red-black-node';
2+
import 'mocha';
3+
import { expect } from 'chai';
4+
import { RedBlackTree } from '../../../src/ts/index';
5+
6+
describe('RedBlackTree', () => {
7+
let tree: RedBlackTree<number>;
8+
9+
beforeEach(() => {
10+
tree = new RedBlackTree<number>();
11+
});
12+
13+
it('starts empty', () => {
14+
expect(tree.getRoot()).to.equal(undefined);
15+
});
16+
17+
it('inserts elements in the RedBlackTree', () => {
18+
expect(tree.getRoot()).to.equal(undefined);
19+
20+
let node;
21+
22+
tree.insert(1);
23+
assertNode(tree.getRoot(), 1, Colors.BLACK);
24+
25+
tree.insert(2);
26+
assertNode(tree.getRoot(), 1, Colors.BLACK);
27+
assertNode(tree.getRoot().right, 2, Colors.RED);
28+
29+
tree.insert(3);
30+
assertNode(tree.getRoot(), 2, Colors.BLACK);
31+
assertNode(tree.getRoot().right, 3, Colors.RED);
32+
assertNode(tree.getRoot().left, 1, Colors.RED);
33+
34+
tree.insert(4);
35+
assertNode(tree.getRoot(), 2, Colors.BLACK);
36+
assertNode(tree.getRoot().left, 1, Colors.BLACK);
37+
assertNode(tree.getRoot().right, 3, Colors.BLACK);
38+
assertNode(tree.getRoot().right.right, 4, Colors.RED);
39+
40+
tree.insert(5);
41+
assertNode(tree.getRoot(), 2, Colors.BLACK);
42+
assertNode(tree.getRoot().left, 1, Colors.BLACK);
43+
node = tree.getRoot().right;
44+
assertNode(node, 4, Colors.BLACK);
45+
assertNode(node.left, 3, Colors.RED);
46+
assertNode(node.right, 5, Colors.RED);
47+
48+
tree.insert(6);
49+
assertNode(tree.getRoot(), 2, Colors.BLACK);
50+
assertNode(tree.getRoot().left, 1, Colors.BLACK);
51+
node = tree.getRoot().right;
52+
assertNode(node, 4, Colors.RED);
53+
assertNode(node.left, 3, Colors.BLACK);
54+
assertNode(node.right, 5, Colors.BLACK);
55+
assertNode(node.right.right, 6, Colors.RED);
56+
57+
tree.insert(7);
58+
assertNode(tree.getRoot(), 2, Colors.BLACK);
59+
assertNode(tree.getRoot().left, 1, Colors.BLACK);
60+
node = tree.getRoot().right;
61+
assertNode(node, 4, Colors.RED);
62+
assertNode(node.left, 3, Colors.BLACK);
63+
assertNode(node.right, 6, Colors.BLACK);
64+
assertNode(node.right.right, 7, Colors.RED);
65+
assertNode(node.right.left, 5, Colors.RED);
66+
67+
tree.insert(8);
68+
assertNode(tree.getRoot(), 4, Colors.BLACK);
69+
node = tree.getRoot().left;
70+
assertNode(node, 2, Colors.RED);
71+
assertNode(node.left, 1, Colors.BLACK);
72+
assertNode(node.right, 3, Colors.BLACK);
73+
node = tree.getRoot().right;
74+
assertNode(node, 5, Colors.RED);
75+
assertNode(node.left, 5, Colors.BLACK);
76+
assertNode(node.right, 7, Colors.BLACK);
77+
assertNode(node.right.right, 8, Colors.RED);
78+
79+
80+
});
81+
82+
function assertNode(node, key, color) {
83+
expect(node.color).to.equal(color);
84+
expect(node.key).to.equal(key);
85+
}
86+
});

0 commit comments

Comments
 (0)