@live-change/flatten-interval-tree 0.8.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +8 -0
- package/.travis.yml +14 -0
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/dist/main.cjs.js +799 -0
- package/dist/main.esm.js +794 -0
- package/dist/main.umd.js +805 -0
- package/docs/Interval.html +1609 -0
- package/docs/IntervalTree.html +1506 -0
- package/docs/classes_interval.js.html +173 -0
- package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Bold-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Italic-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Light-webfont.svg +1831 -0
- package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Regular-webfont.svg +1831 -0
- package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Semibold-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Semibold-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-Semibold-webfont.ttf +0 -0
- package/docs/fonts/OpenSans-Semibold-webfont.woff +0 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf +0 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.woff +0 -0
- package/docs/index.html +197 -0
- package/docs/index.js.html +624 -0
- package/docs/interval.js.html +184 -0
- package/docs/intervalTree.js.html +624 -0
- package/docs/scripts/linenumber.js +25 -0
- package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/scripts/prettify/lang-css.js +2 -0
- package/docs/scripts/prettify/prettify.js +28 -0
- package/docs/styles/jsdoc-default.css +692 -0
- package/docs/styles/prettify-jsdoc.css +111 -0
- package/docs/styles/prettify-tomorrow.css +132 -0
- package/examples/browser/index.html +49 -0
- package/examples/create-react-app/package.json +15 -0
- package/examples/create-react-app/public/index.html +11 -0
- package/examples/create-react-app/src/App.js +38 -0
- package/examples/create-react-app/src/ComposersList.js +15 -0
- package/examples/create-react-app/src/index.js +5 -0
- package/examples/es6-module/index.html +50 -0
- package/examples/nodejs/index.js +23 -0
- package/index.d.ts +65 -0
- package/index.js +6 -0
- package/package.json +52 -0
- package/rollup.config.js +21 -0
- package/src/classes/interval.js +131 -0
- package/src/classes/intervalTree.js +564 -0
- package/src/classes/node.js +97 -0
- package/src/utils/constants.js +16 -0
- package/test/intervalTree.js +232 -0
- package/test/node.js +64 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>intervalTree.js - Documentation</title>
|
|
7
|
+
|
|
8
|
+
<script src="scripts/prettify/prettify.js"></script>
|
|
9
|
+
<script src="scripts/prettify/lang-css.js"></script>
|
|
10
|
+
<!--[if lt IE 9]>
|
|
11
|
+
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
12
|
+
<![endif]-->
|
|
13
|
+
<link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
|
|
14
|
+
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
|
15
|
+
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
|
|
19
|
+
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
|
|
20
|
+
<label for="nav-trigger" class="navicon-button x">
|
|
21
|
+
<div class="navicon"></div>
|
|
22
|
+
</label>
|
|
23
|
+
|
|
24
|
+
<label for="nav-trigger" class="overlay"></label>
|
|
25
|
+
|
|
26
|
+
<nav>
|
|
27
|
+
<li class="nav-link nav-home-link"><a href="index.html">Home</a></li><li class="nav-heading">Classes</li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Interval.html">Interval</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interval.html#.comparable_less_than">comparable_less_than</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interval.html#.comparable_max">comparable_max</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interval.html#clone">clone</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interval.html#equal_to">equal_to</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interval.html#intersect">intersect</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interval.html#less_than">less_than</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interval.html#not_intersect">not_intersect</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interval.html#output">output</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="IntervalTree.html">IntervalTree</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="IntervalTree.html#exist">exist</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="IntervalTree.html#forEach">forEach</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="IntervalTree.html#insert">insert</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="IntervalTree.html#isEmpty">isEmpty</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="IntervalTree.html#map">map</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="IntervalTree.html#remove">remove</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="IntervalTree.html#search">search</a></span></li>
|
|
28
|
+
</nav>
|
|
29
|
+
|
|
30
|
+
<div id="main">
|
|
31
|
+
|
|
32
|
+
<h1 class="page-title">intervalTree.js</h1>
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
<section>
|
|
41
|
+
<article>
|
|
42
|
+
<pre class="prettyprint source linenums"><code>/**
|
|
43
|
+
* Created by Alex Bol on 3/31/2017.
|
|
44
|
+
*/
|
|
45
|
+
'use strict';
|
|
46
|
+
|
|
47
|
+
import Node from './node.js';
|
|
48
|
+
import {RB_TREE_COLOR_RED, RB_TREE_COLOR_BLACK} from '../utils/constants.js';
|
|
49
|
+
|
|
50
|
+
// const nil_node = new Node();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Implementation of interval binary search tree <br/>
|
|
54
|
+
* Interval tree stores items which are couples of {key:interval, value: value} <br/>
|
|
55
|
+
* Interval is an object with high and low properties or simply pair [low,high] of numeric values <br />
|
|
56
|
+
* @type {IntervalTree}
|
|
57
|
+
*/
|
|
58
|
+
class IntervalTree {
|
|
59
|
+
/**
|
|
60
|
+
* Construct new empty instance of IntervalTree
|
|
61
|
+
*/
|
|
62
|
+
constructor() {
|
|
63
|
+
this.root = null;
|
|
64
|
+
this.nil_node = new Node();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Returns number of items stored in the interval tree
|
|
69
|
+
* @returns {number}
|
|
70
|
+
*/
|
|
71
|
+
get size() {
|
|
72
|
+
let count = 0;
|
|
73
|
+
this.tree_walk(this.root, () => count++);
|
|
74
|
+
return count;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Returns array of sorted keys in the ascending order
|
|
79
|
+
* @returns {Array}
|
|
80
|
+
*/
|
|
81
|
+
get keys() {
|
|
82
|
+
let res = [];
|
|
83
|
+
this.tree_walk(this.root, (node) => res.push(
|
|
84
|
+
node.item.key.output ? node.item.key.output() : node.item.key
|
|
85
|
+
));
|
|
86
|
+
return res;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Return array of values in the ascending keys order
|
|
91
|
+
* @returns {Array}
|
|
92
|
+
*/
|
|
93
|
+
get values() {
|
|
94
|
+
let res = [];
|
|
95
|
+
this.tree_walk(this.root, (node) => res.push(node.item.value));
|
|
96
|
+
return res;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Returns array of items (<key,value> pairs) in the ascended keys order
|
|
101
|
+
* @returns {Array}
|
|
102
|
+
*/
|
|
103
|
+
get items() {
|
|
104
|
+
let res = [];
|
|
105
|
+
this.tree_walk(this.root, (node) => res.push({
|
|
106
|
+
key: node.item.key.output ? node.item.key.output() : node.item.key,
|
|
107
|
+
value: node.item.value
|
|
108
|
+
}));
|
|
109
|
+
return res;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Returns true if tree is empty
|
|
114
|
+
* @returns {boolean}
|
|
115
|
+
*/
|
|
116
|
+
isEmpty() {
|
|
117
|
+
return (this.root == null || this.root == this.nil_node);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Insert new item into interval tree
|
|
122
|
+
* @param key - interval object or array of two numbers [low, high]
|
|
123
|
+
* @param value - value representing any object (optional)
|
|
124
|
+
* @returns {Node} - returns reference to inserted node as an object {key:interval, value: value}
|
|
125
|
+
*/
|
|
126
|
+
insert(key, value = key) {
|
|
127
|
+
if (key === undefined) return;
|
|
128
|
+
let insert_node = new Node(key, value, this.nil_node, this.nil_node, null, RB_TREE_COLOR_RED);
|
|
129
|
+
this.tree_insert(insert_node);
|
|
130
|
+
this.recalc_max(insert_node);
|
|
131
|
+
return insert_node;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Returns true if item {key,value} exist in the tree
|
|
136
|
+
* @param key - interval correspondent to keys stored in the tree
|
|
137
|
+
* @param value - value object to be checked
|
|
138
|
+
* @returns {boolean} - true if item {key, value} exist in the tree, false otherwise
|
|
139
|
+
*/
|
|
140
|
+
exist(key, value) {
|
|
141
|
+
let search_node = new Node(key, value);
|
|
142
|
+
return this.tree_search(this.root, search_node) ? true : false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Remove entry {key, value} from the tree
|
|
147
|
+
* @param key - interval correspondent to keys stored in the tree
|
|
148
|
+
* @param value - - value object
|
|
149
|
+
* @returns {boolean} - true if item {key, value} deleted, false if not found
|
|
150
|
+
*/
|
|
151
|
+
remove(key, value) {
|
|
152
|
+
let search_node = new Node(key, value);
|
|
153
|
+
let delete_node = this.tree_search(this.root, search_node);
|
|
154
|
+
if (delete_node) {
|
|
155
|
+
this.tree_delete(delete_node);
|
|
156
|
+
}
|
|
157
|
+
return delete_node;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Returns array of entry values which keys intersect with given interval <br/>
|
|
162
|
+
* If no values stored in the tree, returns array of keys which intersect given interval
|
|
163
|
+
* @param interval - search interval, or array [low, high]
|
|
164
|
+
* @param outputMapperFn(value,key) - optional function that maps (value, key) to custom output
|
|
165
|
+
* @returns {Array}
|
|
166
|
+
*/
|
|
167
|
+
search(interval, outputMapperFn = (value, key) => value ? value : key.output()) {
|
|
168
|
+
let search_node = new Node(interval);
|
|
169
|
+
let resp_nodes = [];
|
|
170
|
+
this.tree_search_interval(this.root, search_node, resp_nodes);
|
|
171
|
+
return resp_nodes.map(node => outputMapperFn(node.item.value, node.item.key))
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Tree visitor. For each node implement a callback function. <br/>
|
|
176
|
+
* Method calls a callback function with two parameters (key, value)
|
|
177
|
+
* @param visitor(key,value) - function to be called for each tree item
|
|
178
|
+
*/
|
|
179
|
+
forEach(visitor) {
|
|
180
|
+
this.tree_walk(this.root, (node) => visitor(node.item.key, node.item.value));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Value Mapper. Walk through every node and map node value to another value
|
|
184
|
+
* @param callback(value, key) - function to be called for each tree item
|
|
185
|
+
*/
|
|
186
|
+
map(callback) {
|
|
187
|
+
const tree = new IntervalTree();
|
|
188
|
+
this.tree_walk(this.root, (node) => tree.insert(node.item.key, callback(node.item.value, node.item.key)));
|
|
189
|
+
return tree;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
recalc_max(node) {
|
|
193
|
+
let node_current = node;
|
|
194
|
+
while (node_current.parent != null) {
|
|
195
|
+
node_current.parent.update_max();
|
|
196
|
+
node_current = node_current.parent;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
tree_insert(insert_node) {
|
|
201
|
+
let current_node = this.root;
|
|
202
|
+
let parent_node = null;
|
|
203
|
+
|
|
204
|
+
if (this.root == null || this.root == this.nil_node) {
|
|
205
|
+
this.root = insert_node;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
while (current_node != this.nil_node) {
|
|
209
|
+
parent_node = current_node;
|
|
210
|
+
if (insert_node.less_than(current_node)) {
|
|
211
|
+
current_node = current_node.left;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
current_node = current_node.right;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
insert_node.parent = parent_node;
|
|
219
|
+
|
|
220
|
+
if (insert_node.less_than(parent_node)) {
|
|
221
|
+
parent_node.left = insert_node;
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
parent_node.right = insert_node;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
this.insert_fixup(insert_node);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// After insertion insert_node may have red-colored parent, and this is a single possible violation
|
|
232
|
+
// Go upwords to the root and re-color until violation will be resolved
|
|
233
|
+
insert_fixup(insert_node) {
|
|
234
|
+
let current_node;
|
|
235
|
+
let uncle_node;
|
|
236
|
+
|
|
237
|
+
current_node = insert_node;
|
|
238
|
+
while (current_node != this.root && current_node.parent.color == RB_TREE_COLOR_RED) {
|
|
239
|
+
if (current_node.parent == current_node.parent.parent.left) { // parent is left child of grandfather
|
|
240
|
+
uncle_node = current_node.parent.parent.right; // right brother of parent
|
|
241
|
+
if (uncle_node.color == RB_TREE_COLOR_RED) { // Case 1. Uncle is red
|
|
242
|
+
// re-color father and uncle into black
|
|
243
|
+
current_node.parent.color = RB_TREE_COLOR_BLACK;
|
|
244
|
+
uncle_node.color = RB_TREE_COLOR_BLACK;
|
|
245
|
+
current_node.parent.parent.color = RB_TREE_COLOR_RED;
|
|
246
|
+
current_node = current_node.parent.parent;
|
|
247
|
+
}
|
|
248
|
+
else { // Case 2 & 3. Uncle is black
|
|
249
|
+
if (current_node == current_node.parent.right) { // Case 2. Current if right child
|
|
250
|
+
// This case is transformed into Case 3.
|
|
251
|
+
current_node = current_node.parent;
|
|
252
|
+
this.rotate_left(current_node);
|
|
253
|
+
}
|
|
254
|
+
current_node.parent.color = RB_TREE_COLOR_BLACK; // Case 3. Current is left child.
|
|
255
|
+
// Re-color father and grandfather, rotate grandfather right
|
|
256
|
+
current_node.parent.parent.color = RB_TREE_COLOR_RED;
|
|
257
|
+
this.rotate_right(current_node.parent.parent);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else { // parent is right child of grandfather
|
|
261
|
+
uncle_node = current_node.parent.parent.left; // left brother of parent
|
|
262
|
+
if (uncle_node.color == RB_TREE_COLOR_RED) { // Case 4. Uncle is red
|
|
263
|
+
// re-color father and uncle into black
|
|
264
|
+
current_node.parent.color = RB_TREE_COLOR_BLACK;
|
|
265
|
+
uncle_node.color = RB_TREE_COLOR_BLACK;
|
|
266
|
+
current_node.parent.parent.color = RB_TREE_COLOR_RED;
|
|
267
|
+
current_node = current_node.parent.parent;
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
if (current_node == current_node.parent.left) { // Case 5. Current is left child
|
|
271
|
+
// Transform into case 6
|
|
272
|
+
current_node = current_node.parent;
|
|
273
|
+
this.rotate_right(current_node);
|
|
274
|
+
}
|
|
275
|
+
current_node.parent.color = RB_TREE_COLOR_BLACK; // Case 6. Current is right child.
|
|
276
|
+
// Re-color father and grandfather, rotate grandfather left
|
|
277
|
+
current_node.parent.parent.color = RB_TREE_COLOR_RED;
|
|
278
|
+
this.rotate_left(current_node.parent.parent);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
this.root.color = RB_TREE_COLOR_BLACK;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
tree_delete(delete_node) {
|
|
287
|
+
let cut_node; // node to be cut - either delete_node or successor_node ("y" from 14.4)
|
|
288
|
+
let fix_node; // node to fix rb tree property ("x" from 14.4)
|
|
289
|
+
|
|
290
|
+
if (delete_node.left == this.nil_node || delete_node.right == this.nil_node) { // delete_node has less then 2 children
|
|
291
|
+
cut_node = delete_node;
|
|
292
|
+
}
|
|
293
|
+
else { // delete_node has 2 children
|
|
294
|
+
cut_node = this.tree_successor(delete_node);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// fix_node if single child of cut_node
|
|
298
|
+
if (cut_node.left != this.nil_node) {
|
|
299
|
+
fix_node = cut_node.left;
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
fix_node = cut_node.right;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// remove cut_node from parent
|
|
306
|
+
/*if (fix_node != this.nil_node) {*/
|
|
307
|
+
fix_node.parent = cut_node.parent;
|
|
308
|
+
/*}*/
|
|
309
|
+
|
|
310
|
+
if (cut_node == this.root) {
|
|
311
|
+
this.root = fix_node;
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
if (cut_node == cut_node.parent.left) {
|
|
315
|
+
cut_node.parent.left = fix_node;
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
cut_node.parent.right = fix_node;
|
|
319
|
+
}
|
|
320
|
+
cut_node.parent.update_max(); // update max property of the parent
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
this.recalc_max(fix_node); // update max property upward from fix_node to root
|
|
324
|
+
|
|
325
|
+
// COPY DATA !!!
|
|
326
|
+
// Delete_node becomes cut_node, it means that we cannot hold reference
|
|
327
|
+
// to node in outer structure and we will have to delete by key, additional search need
|
|
328
|
+
if (cut_node != delete_node) {
|
|
329
|
+
delete_node.copy_data(cut_node);
|
|
330
|
+
delete_node.update_max(); // update max property of the cut node at the new place
|
|
331
|
+
this.recalc_max(delete_node); // update max property upward from delete_node to root
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (/*fix_node != this.nil_node && */cut_node.color == RB_TREE_COLOR_BLACK) {
|
|
335
|
+
this.delete_fixup(fix_node);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
delete_fixup(fix_node) {
|
|
340
|
+
let current_node = fix_node;
|
|
341
|
+
let brother_node;
|
|
342
|
+
|
|
343
|
+
while (current_node != this.root && current_node.parent != null && current_node.color == RB_TREE_COLOR_BLACK) {
|
|
344
|
+
if (current_node == current_node.parent.left) { // fix node is left child
|
|
345
|
+
brother_node = current_node.parent.right;
|
|
346
|
+
if (brother_node.color == RB_TREE_COLOR_RED) { // Case 1. Brother is red
|
|
347
|
+
brother_node.color = RB_TREE_COLOR_BLACK; // re-color brother
|
|
348
|
+
current_node.parent.color = RB_TREE_COLOR_RED; // re-color father
|
|
349
|
+
this.rotate_left(current_node.parent);
|
|
350
|
+
brother_node = current_node.parent.right; // update brother
|
|
351
|
+
}
|
|
352
|
+
// Derive to cases 2..4: brother is black
|
|
353
|
+
if (brother_node.left.color == RB_TREE_COLOR_BLACK &&
|
|
354
|
+
brother_node.right.color == RB_TREE_COLOR_BLACK) { // case 2: both nephews black
|
|
355
|
+
brother_node.color = RB_TREE_COLOR_RED; // re-color brother
|
|
356
|
+
current_node = current_node.parent; // continue iteration
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
if (brother_node.right.color == RB_TREE_COLOR_BLACK) { // case 3: left nephew red, right nephew black
|
|
360
|
+
brother_node.color = RB_TREE_COLOR_RED; // re-color brother
|
|
361
|
+
brother_node.left.color = RB_TREE_COLOR_BLACK; // re-color nephew
|
|
362
|
+
this.rotate_right(brother_node);
|
|
363
|
+
brother_node = current_node.parent.right; // update brother
|
|
364
|
+
// Derive to case 4: left nephew black, right nephew red
|
|
365
|
+
}
|
|
366
|
+
// case 4: left nephew black, right nephew red
|
|
367
|
+
brother_node.color = current_node.parent.color;
|
|
368
|
+
current_node.parent.color = RB_TREE_COLOR_BLACK;
|
|
369
|
+
brother_node.right.color = RB_TREE_COLOR_BLACK;
|
|
370
|
+
this.rotate_left(current_node.parent);
|
|
371
|
+
current_node = this.root; // exit from loop
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
else { // fix node is right child
|
|
375
|
+
brother_node = current_node.parent.left;
|
|
376
|
+
if (brother_node.color == RB_TREE_COLOR_RED) { // Case 1. Brother is red
|
|
377
|
+
brother_node.color = RB_TREE_COLOR_BLACK; // re-color brother
|
|
378
|
+
current_node.parent.color = RB_TREE_COLOR_RED; // re-color father
|
|
379
|
+
this.rotate_right(current_node.parent);
|
|
380
|
+
brother_node = current_node.parent.left; // update brother
|
|
381
|
+
}
|
|
382
|
+
// Go to cases 2..4
|
|
383
|
+
if (brother_node.left.color == RB_TREE_COLOR_BLACK &&
|
|
384
|
+
brother_node.right.color == RB_TREE_COLOR_BLACK) { // case 2
|
|
385
|
+
brother_node.color = RB_TREE_COLOR_RED; // re-color brother
|
|
386
|
+
current_node = current_node.parent; // continue iteration
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
if (brother_node.left.color == RB_TREE_COLOR_BLACK) { // case 3: right nephew red, left nephew black
|
|
390
|
+
brother_node.color = RB_TREE_COLOR_RED; // re-color brother
|
|
391
|
+
brother_node.right.color = RB_TREE_COLOR_BLACK; // re-color nephew
|
|
392
|
+
this.rotate_left(brother_node);
|
|
393
|
+
brother_node = current_node.parent.left; // update brother
|
|
394
|
+
// Derive to case 4: right nephew black, left nephew red
|
|
395
|
+
}
|
|
396
|
+
// case 4: right nephew black, left nephew red
|
|
397
|
+
brother_node.color = current_node.parent.color;
|
|
398
|
+
current_node.parent.color = RB_TREE_COLOR_BLACK;
|
|
399
|
+
brother_node.left.color = RB_TREE_COLOR_BLACK;
|
|
400
|
+
this.rotate_right(current_node.parent);
|
|
401
|
+
current_node = this.root; // force exit from loop
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
current_node.color = RB_TREE_COLOR_BLACK;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
tree_search(node, search_node) {
|
|
410
|
+
if (node == null || node == this.nil_node)
|
|
411
|
+
return undefined;
|
|
412
|
+
|
|
413
|
+
if (search_node.equal_to(node)) {
|
|
414
|
+
return node;
|
|
415
|
+
}
|
|
416
|
+
if (search_node.less_than(node)) {
|
|
417
|
+
return this.tree_search(node.left, search_node);
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
return this.tree_search(node.right, search_node);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Original search_interval method; container res support push() insertion
|
|
425
|
+
// Search all intervals intersecting given one
|
|
426
|
+
tree_search_interval(node, search_node, res) {
|
|
427
|
+
if (node != null && node != this.nil_node) {
|
|
428
|
+
// if (node->left != this.nil_node && node->left->max >= low) {
|
|
429
|
+
if (node.left != this.nil_node && !node.not_intersect_left_subtree(search_node)) {
|
|
430
|
+
this.tree_search_interval(node.left, search_node, res);
|
|
431
|
+
}
|
|
432
|
+
// if (low <= node->high && node->low <= high) {
|
|
433
|
+
if (node.intersect(search_node)) {
|
|
434
|
+
res.push(node);
|
|
435
|
+
}
|
|
436
|
+
// if (node->right != this.nil_node && node->low <= high) {
|
|
437
|
+
if (node.right != this.nil_node && !node.not_intersect_right_subtree(search_node)) {
|
|
438
|
+
this.tree_search_interval(node.right, search_node, res);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
local_minimum(node) {
|
|
444
|
+
let node_min = node;
|
|
445
|
+
while (node_min.left != null && node_min.left != this.nil_node) {
|
|
446
|
+
node_min = node_min.left;
|
|
447
|
+
}
|
|
448
|
+
return node_min;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// not in use
|
|
452
|
+
local_maximum(node) {
|
|
453
|
+
let node_max = node;
|
|
454
|
+
while (node_max.right != null && node_max.right != this.nil_node) {
|
|
455
|
+
node_max = node_max.right;
|
|
456
|
+
}
|
|
457
|
+
return node_max;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
tree_successor(node) {
|
|
461
|
+
let node_successor;
|
|
462
|
+
let current_node;
|
|
463
|
+
let parent_node;
|
|
464
|
+
|
|
465
|
+
if (node.right != this.nil_node) {
|
|
466
|
+
node_successor = this.local_minimum(node.right);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
current_node = node;
|
|
470
|
+
parent_node = node.parent;
|
|
471
|
+
while (parent_node != null && parent_node.right == current_node) {
|
|
472
|
+
current_node = parent_node;
|
|
473
|
+
parent_node = parent_node.parent;
|
|
474
|
+
}
|
|
475
|
+
node_successor = parent_node;
|
|
476
|
+
}
|
|
477
|
+
return node_successor;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// | right-rotate(T,y) |
|
|
481
|
+
// y ---------------. x
|
|
482
|
+
// / \ / \
|
|
483
|
+
// x c left-rotate(T,x) a y
|
|
484
|
+
// / \ <--------------- / \
|
|
485
|
+
// a b b c
|
|
486
|
+
|
|
487
|
+
rotate_left(x) {
|
|
488
|
+
let y = x.right;
|
|
489
|
+
|
|
490
|
+
x.right = y.left; // b goes to x.right
|
|
491
|
+
|
|
492
|
+
if (y.left != this.nil_node) {
|
|
493
|
+
y.left.parent = x; // x becomes parent of b
|
|
494
|
+
}
|
|
495
|
+
y.parent = x.parent; // move parent
|
|
496
|
+
|
|
497
|
+
if (x == this.root) {
|
|
498
|
+
this.root = y; // y becomes root
|
|
499
|
+
}
|
|
500
|
+
else { // y becomes child of x.parent
|
|
501
|
+
if (x == x.parent.left) {
|
|
502
|
+
x.parent.left = y;
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
x.parent.right = y;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
y.left = x; // x becomes left child of y
|
|
509
|
+
x.parent = y; // and y becomes parent of x
|
|
510
|
+
|
|
511
|
+
if (x != null && x != this.nil_node) {
|
|
512
|
+
x.update_max();
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
y = x.parent;
|
|
516
|
+
if (y != null && y != this.nil_node) {
|
|
517
|
+
y.update_max();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
rotate_right(y) {
|
|
522
|
+
let x = y.left;
|
|
523
|
+
|
|
524
|
+
y.left = x.right; // b goes to y.left
|
|
525
|
+
|
|
526
|
+
if (x.right != this.nil_node) {
|
|
527
|
+
x.right.parent = y; // y becomes parent of b
|
|
528
|
+
}
|
|
529
|
+
x.parent = y.parent; // move parent
|
|
530
|
+
|
|
531
|
+
if (y == this.root) { // x becomes root
|
|
532
|
+
this.root = x;
|
|
533
|
+
}
|
|
534
|
+
else { // y becomes child of x.parent
|
|
535
|
+
if (y == y.parent.left) {
|
|
536
|
+
y.parent.left = x;
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
y.parent.right = x;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
x.right = y; // y becomes right child of x
|
|
543
|
+
y.parent = x; // and x becomes parent of y
|
|
544
|
+
|
|
545
|
+
if (y != null && y != this.nil_node) {
|
|
546
|
+
y.update_max();
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
x = y.parent;
|
|
550
|
+
if (x != null && x != this.nil_node) {
|
|
551
|
+
x.update_max();
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
tree_walk(node, action) {
|
|
556
|
+
if (node != null && node != this.nil_node) {
|
|
557
|
+
this.tree_walk(node.left, action);
|
|
558
|
+
// arr.push(node.toArray());
|
|
559
|
+
action(node);
|
|
560
|
+
this.tree_walk(node.right, action);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/* Return true if all red nodes have exactly two black child nodes */
|
|
565
|
+
testRedBlackProperty() {
|
|
566
|
+
let res = true;
|
|
567
|
+
this.tree_walk(this.root, function (node) {
|
|
568
|
+
if (node.color == RB_TREE_COLOR_RED) {
|
|
569
|
+
if (!(node.left.color == RB_TREE_COLOR_BLACK && node.right.color == RB_TREE_COLOR_BLACK)) {
|
|
570
|
+
res = false;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
return res;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/* Throw error if not every path from root to bottom has same black height */
|
|
578
|
+
testBlackHeightProperty(node) {
|
|
579
|
+
let height = 0;
|
|
580
|
+
let heightLeft = 0;
|
|
581
|
+
let heightRight = 0;
|
|
582
|
+
if (node.color == RB_TREE_COLOR_BLACK) {
|
|
583
|
+
height++;
|
|
584
|
+
}
|
|
585
|
+
if (node.left != this.nil_node) {
|
|
586
|
+
heightLeft = this.testBlackHeightProperty(node.left);
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
heightLeft = 1;
|
|
590
|
+
}
|
|
591
|
+
if (node.right != this.nil_node) {
|
|
592
|
+
heightRight = this.testBlackHeightProperty(node.right);
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
heightRight = 1;
|
|
596
|
+
}
|
|
597
|
+
if (heightLeft != heightRight) {
|
|
598
|
+
throw new Error('Red-black height property violated');
|
|
599
|
+
}
|
|
600
|
+
height += heightLeft;
|
|
601
|
+
return height;
|
|
602
|
+
};
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
export default IntervalTree;
|
|
606
|
+
</code></pre>
|
|
607
|
+
</article>
|
|
608
|
+
</section>
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
</div>
|
|
614
|
+
|
|
615
|
+
<br class="clear">
|
|
616
|
+
|
|
617
|
+
<footer>
|
|
618
|
+
Generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Sat Mar 16 2019 12:06:26 GMT+0200 (Israel Standard Time) using the Minami theme.
|
|
619
|
+
</footer>
|
|
620
|
+
|
|
621
|
+
<script>prettyPrint();</script>
|
|
622
|
+
<script src="scripts/linenumber.js"></script>
|
|
623
|
+
</body>
|
|
624
|
+
</html>
|