Skip to content

Commit ee1bafc

Browse files
committed
Add Red Black Tree Implementation
Very sophiscated Red Black Tree implementation that mimic MySQL indexing with extended capability to accept number and string as key. The implementation based on most recent Red Black Tree algorithm, "Left-Leaning Red Black Trees" invented by Robert Sedgewick at 2008. Design of API based on Java implementation by Kevin Wayne (https://github.com/kevin-wayne/algs4/blob/master/src/main/java/edu/princeton/cs/algs4/RedBlackBST.java). Fix issue: trekhleb#29
1 parent 63e451e commit ee1bafc

File tree

5 files changed

+972
-0
lines changed

5 files changed

+972
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Red Black Tree
2+
3+
Red-black tree is most popular self-balancing binary search tree algorithm. First introduced by [Leonidas J. Guibas](https://en.wikipedia.org/wiki/Leonidas_J._Guibas) and [Robert Sedgewick](https://en.wikipedia.org/wiki/Robert_Sedgewick_(computer_scientist)) in the 1978 paper "A Dichromatic Framework for Balanced Trees". Their work inspired by previous work by [Rudolf Bayer](https://en.wikipedia.org/wiki/Rudolf_Bayer) at 1972. The most recent variant of red-black tree is Left-Lining Red-Black (LLRB) tree invented by [Robert Sedgewick](https://en.wikipedia.org/wiki/Robert_Sedgewick_(computer_scientist)) in the 2008 paper ["Left-leaning Red-Black Trees"](https://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf).
4+
5+
Basically, red-black tree is improved version of [2-3 tree](https://en.wikipedia.org/wiki/2%E2%80%933_tree) by represent 2-3 tree as binary search tree. Red-black tree use *red link* to bind together 2-nodes to represent 3-nodes, then connecting each childs to its parent using *black link*. Consider an image below:
6+
7+
![2-3 nodes in red-blac BST](https://algs4.cs.princeton.edu/33balanced/images/redblack-encoding.png)
8+
9+
The first part of image (above) is 3-nodes representation in 2-3 tree, while the second part of image (below) is 2-nodes representation using red and black links.
10+
11+
## Basic Operation
12+
13+
### Find
14+
15+
Since red-black tree break down 3-nodes into 2-nodes representation, the find operation similar to find operation in regular binary search tree.
16+
17+
### Flipping Colors and Rotation
18+
19+
Flipping color is key concept in red-black tree in order to maintain balance of tree while rotation executed when insertion and deletion occured. Consider three images below with their implementation in Java:
20+
21+
##### Left Rotation
22+
23+
![Left rotation](https://algs4.cs.princeton.edu/33balanced/images/redblack-left-rotate.png)
24+
25+
##### Right Rotation
26+
27+
![Right rotation](https://algs4.cs.princeton.edu/33balanced/images/redblack-right-rotate.png)
28+
29+
#### Fliping Colors
30+
31+
![Flipping colors](https://algs4.cs.princeton.edu/33balanced/images/color-flip.png)
32+
33+
Notice that when subtree is balanced, parent's colors flipped from red to black.
34+
35+
## Insertion
36+
37+
Just keep in mind that:
38+
39+
* When insertion caused tree has incomplete leaves, then each unbalanced tree has red link.
40+
* When insertion caused tree has complete leaves, then all red links dismissed.
41+
42+
![Red-black tree insertion](https://algs4.cs.princeton.edu/33balanced/images/redblack-construction.png)
43+
44+
An interactive animation of left-leaning red-black tree can be found at [here](http://inst.eecs.berkeley.edu/~cs61b/fa17/materials/demos/ll-red-black-demo.html)
45+
46+
## Performance
47+
48+
### Worst case
49+
50+
* search: `2 lg N`
51+
* insert: `2 lg N`
52+
* delete: `2 lg N`
53+
54+
### Average case
55+
56+
* search: `~ 1.00 lg N`
57+
* insert: `~ 1.00 lg N`
58+
* delete: `~ 1.00 lg N`
59+
60+
## Implementation in Software Industry
61+
62+
MySQL used Red-Black tree to store index keys in memory and key value into B-Tree. When memory reach its limits, MySQL writes all keys to disk.
63+
64+
65+
## References
66+
* [Algorithm 4 edition](https://algs4.cs.princeton.edu/33balanced/)
67+
* [Algorithm course slides](https://drive.google.com/file/d/1XC5qTc-9XdR2waDoNnxD-FH66ocFX2DO/view)
68+
* [Bulk insert MySQL](https://dev.mysql.com/doc/internals/en/bulk-insert.html)
69+
* [The Physical Structure of an InnoDB Index](https://dev.mysql.com/doc/refman/8.0/en/innodb-physical-structure.html)
70+
* [wikipedia](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Segment id distance of A to z is 57. Add 1 became 58.
3+
* If each char has 8 length at most, then there is a combination 58^8 = 128063081718016.
4+
* Then all combination is 58^9 = 7427658739644928.
5+
*/
6+
const STRING_ENCODING_LENGTH = 8;
7+
const STRING_SEGMENT = 58;
8+
const STRING_COMBINATION = STRING_SEGMENT ** STRING_ENCODING_LENGTH;
9+
10+
export default class RedBlackNode {
11+
/**
12+
* Red-black tree node constructor.
13+
* Each node has key->value to facilitate table lookup.
14+
* @constructor
15+
* @param {(string|number)} key - key or index.
16+
* @param {object} value - value.
17+
* @param {boolean} color - parent's color.
18+
*/
19+
constructor(key = null, value = null, color = null, size = 1) {
20+
this.left = null;
21+
this.right = null;
22+
if (typeof key === 'string') {
23+
if (key.length > STRING_ENCODING_LENGTH) {
24+
throw new Error('String type key exceed STRING_ENCODING_LENGTH');
25+
}
26+
}
27+
this.key = key;
28+
this.value = value;
29+
this.color = color; // Color of parent link
30+
this.size = size; // Size of subtree
31+
}
32+
33+
decodeStrKey(key) {
34+
if (typeof key !== 'string') {
35+
throw new Error('Input should be string');
36+
}
37+
38+
let k = 0;
39+
if (key.length > 1) {
40+
k = [...key.slice(1)]
41+
.map(char => char.charCodeAt(0) - 64)
42+
.reduce((current, previous) => current + previous);
43+
}
44+
k += (key.charCodeAt(0) - 64) * STRING_COMBINATION;
45+
46+
return k;
47+
}
48+
49+
/**
50+
* Compare two node based on their keys
51+
* @param {object} node - node to compare.
52+
* @returns {Number} comparation result.
53+
*/
54+
compareTo(node) {
55+
if (typeof this.key === 'number' && typeof node.key === 'number') {
56+
if (this.key > node.key) return 1;
57+
if (this.key < node.key) return -1;
58+
return 0;
59+
}
60+
if (typeof this.key === 'string' && typeof node.key === 'string') {
61+
// Compare based on sum of char codes
62+
const key1 = this.decodeStrKey(this.key);
63+
const key2 = this.decodeStrKey(node.key);
64+
65+
if (key1 > key2) return 1;
66+
if (key1 < key2) return -1;
67+
return 0;
68+
}
69+
const t1 = typeof this.key;
70+
const t2 = typeof node.key;
71+
throw new Error(`Not consistent comparation type: ${t1} versus ${t2}`);
72+
}
73+
}

0 commit comments

Comments
 (0)