Skip to content

Commit 04b5e12

Browse files
committedFeb 21, 2019
maps read prooved
1 parent 570bb8a commit 04b5e12

File tree

6 files changed

+445
-18
lines changed

6 files changed

+445
-18
lines changed
 

‎book/book.adoc

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,19 @@ include::chapters/tree.adoc[]
7979
// (g)
8080
include::chapters/binary-search-tree.adoc[]
8181

82-
// ()
83-
include::chapters/map.adoc[]
82+
// (g)
83+
// include::chapters/map.adoc[]
84+
include::chapters/map-intro.adoc[]
85+
86+
:leveloffset: +1
87+
88+
include::chapters/map-hashmap.adoc[]
89+
90+
include::chapters/map-treemap.adoc[]
91+
92+
include::chapters/map-hashmap-vs-treemap.adoc[]
93+
94+
:leveloffset: -1
8495

8596
include::chapters/set.adoc[]
8697

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
= HashMap vs TreeMap
2+
3+
.A map can be implemented using hash functions or binary search tree:
4+
- *HashMap*: it’s a map implementation using an *array* and *hash function*. The job of the hash function is to convert the key into an index that contains the matching data. Optimized HashMap can have an average runtime of *O(1)*.
5+
- *TreeMap*: it’s a map implementation that uses a self-balanced Binary Search Tree (red-black tree). The BST nodes store the key, and the value and nodes are sorted by key guaranteeing an *O(log n)* look up.
6+
7+
8+
.When to use a TreeMap vs. HashMap?
9+
* `HashMap` is more time-efficient. A `TreeMap` is more space-efficient.
10+
* `TreeMap` search complexity is *O(log n)*, while an optimized `HashMap` is *O(1)* on average. 
11+
* `HashMap`’s keys are in insertion order (or random in some implementations). `TreeMap`’s keys are always sorted.
12+
* `TreeMap` offers some statistical data for free such as: get minimum, get maximum, median, find ranges of keys. `HashMap` doesn’t.
13+
* `TreeMap` has a guarantee always an *O(log n)*, while `HashMap`s has an amortized time of *O(1)* but in the rare case of a rehash, it would take an *O(n)*.
14+
15+
== TreeMap Time complexity vs HashMap
16+
17+
As we discussed so far, there are trade-off between the implementations
18+
19+
.Time complexity for different Maps implementations
20+
|===
21+
.2+.^s| Data Structure 2+^s| Searching By .2+^.^s| Insert .2+^.^s| Delete .2+^.^s| Space Complexity
22+
^|_Index/Key_ ^|_Value_
23+
| Hash Map (naïve) ^|O(n) ^|O(n) ^|O(n) ^|O(n) ^|O(n)
24+
| Hash Map (optimized) ^|O(1)* ^|O(n) ^|O(1)* ^|O(1)* ^|O(1)*
25+
| Tree Map (Red-Black Tree) ^|O(log n) ^|O(n) ^|O(log n) ^|O(log n) ^|O(log n)
26+
|===
27+
{empty}* = Amortized run time. E.g. rehashing might affect run time to *O(n)*.

‎book/chapters/map-hashmap.adoc

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
= HashMap
2+
3+
A HashMap is a Map implementation. HashMaps are composed of two things:
4+
1) a hash function and
5+
2) a bucket array to store values.
6+
7+
Before going into the implementation details let’s give an overview of how it works. Let’s say we want to keep a tally of things:
8+
9+
.HashMap example
10+
[source, javascript]
11+
----
12+
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=snippet, indent=0]
13+
----
14+
15+
How are the keys mapped to their values?
16+
Using a hash function. Here’s an illustration:
17+
18+
.HashMap representation. Keys are mapped to values using a hash function.
19+
image:image41.png[image,width=528,height=299]
20+
21+
22+
.This is the main idea:
23+
1. We use a *hash function* to transform the keys (e.g., dog, cat, rat, …) into an array index. This array is called *bucket*.
24+
2. The bucket holds the values (linked list in case of collisions).
25+
26+
In the illustration, we have a bucket size of 10. In bucket 0, we have a collision. Both `cat` and `art` keys map to the same bucket even thought their hash codes are different.
27+
28+
In a HashMap, a *collision* is when different keys lead to the same index. They are nasty for performance since it can reduce the search time from *O(1)* to *O(n)*.
29+
30+
Having a big bucket size can avoid a collision but also can waste too much memory. We are going to build an _optimized_ HashMap that re-sizes itself when it is getting full. This avoids collisions and doesn’t spend too much memory upfront. Let’s start with the hash function.
31+
32+
== Designing an optimized hash function
33+
34+
To minimize collisions, we need to create an excellent hash function.
35+
36+
IMPORTANT: A *perfect* hash function is one that assigns a unique array index for every different key.
37+
38+
It’s no practical and memory-wise wasteful to have a perfect hash function, so we are going to shoot for a cost-effective hash function instead.
39+
40+
.To recap:
41+
- A hash function converts keys into array indices.
42+
- A hash function is composed of two parts:
43+
1. *Hash Code*: maps any key into an integer (unbonded)
44+
2. *Compression function*: maps an arbitrary integer to integer in the range of [0… BUCKET_SIZE -1].
45+
46+
Before doing a great hash function, let's see what a lousy hash function looks like. 😉
47+
48+
=== Analysing collisions on bad hash code functions
49+
50+
The goal of a hash code function is to convert any value given into a positive integer — a common way to accomplish with summing each string’s Unicode value.
51+
52+
.Naïve hashing function implementation
53+
[source, javascript]
54+
----
55+
include::{codedir}/data-structures/maps/hash-maps/hashing.js[tag=naiveHashCode, indent=0]
56+
----
57+
58+
59+
This function uses `codePointAt` to get the Unicode value of a character. E.g., `a` has a value of 97, `A` is 65, even https://en.wikipedia.org/wiki/Emoji#Unicode_blocks[emojis have codes]; “[big]#😁#” is `128513`.
60+
61+
.JavaScript built-in `string.charCodeAt` vs. `string.codePointAt`
62+
****
63+
The `charCodeAt()` method returns an integer between `0` and `65535` representing the UTF-16 code unit at the given index. However, it doesn’t play nice with Unicode, so it’s better to use `codePointAt` instead.
64+
65+
The `codePointAt()` method returns a non-negative integer that is the Unicode code point value.
66+
****
67+
With this function we have the can convert some keys to numbers as follows:
68+
69+
.Hashing examples
70+
[source, javascript]
71+
----
72+
include::{codedir}/data-structures/maps/hash-maps/hashing.js[tag=naiveHashCodeExamples, indent=0]
73+
----
74+
75+
Notice that `rat` and `art` have the same hash code! These are collisions that we need to solve.
76+
77+
Collisions happened because we are just summing the character's codes and are not taking the order into account nor the type. We can do better by offsetting the character value based on their position in the string. We can also add the object type, so number `10` produce different output than string `'10'`.
78+
79+
.Hashing function implementation that offset character value based on the position
80+
[source, javascript]
81+
----
82+
include::{codedir}/data-structures/maps/hash-maps/hashing.js[tag=hashCodeOffset, indent=0]
83+
----
84+
85+
Since Unicode uses 20 bits, we can offset each character by 20 bits based on the position.
86+
87+
.JavaScript built-in `BigInt`
88+
****
89+
BigInt allows to operate beyond the maximum safe limit of integers (Number.MAX_SAFE_INTEGER => 9,007,199,254,740,991). BigInt uses the suffix n, e.g. 1n + 3n === 4n.
90+
****
91+
92+
As you can imagine the output is a humongous number! We are using `BigInt` that doesn’t overflow.
93+
94+
.Verifying there's not hashing code duplicates
95+
[source, javascript]
96+
----
97+
include::{codedir}/data-structures/maps/hash-maps/hashing.js[tag=hashCodeOffsetExample, indent=0]
98+
----
99+
100+
As you can see We don’t have duplicates if the keys have different content or type. However, we need to represent these unbounded integers. We do that using *compression function* they can be as simple as `% BUCKET_SIZE`.
101+
102+
However, there’s an issue with the last implementation. It doesn’t matter how humongous is the number if we at the end use the modulus to get an array index. The part of the hash code that truly matters is the last bits.
103+
104+
.Look at this example with a bucket size of 4.
105+
[source, javascript]
106+
----
107+
10 % 4 //↪️ 2
108+
20 % 4 //↪️ 0
109+
30 % 4 //↪️ 2
110+
40 % 4 //↪️ 0
111+
50 % 4 //↪️ 2
112+
----
113+
114+
We get many collisions. [big]#😱#
115+
116+
Based on statistical data, using a prime number as the modulus produce fewer collisions.
117+
118+
.Let’s see what happens if the bucket size is a prime number:
119+
[source, javascript]
120+
----
121+
10 % 7 //↪️ 3
122+
20 % 7 //↪️ 6
123+
30 % 7 //↪️ 2
124+
40 % 7 //↪️ 4
125+
50 % 7 //↪️ 1
126+
----
127+
128+
Now it’s more evenly distributed!! [big]#😎👍#
129+
130+
.So, to sum up:
131+
* Bucket size should always be a *prime number*, so data is distributed more evenly and minimized collisions.
132+
* Hash code doesn’t have to be too big. At the end what matters is the few last digits.
133+
134+
Let’s design a better HashMap with what we learned.
135+
136+
=== Implementing an optimized hash function
137+
138+
Take a look at the following function:
139+
140+
.Optimal Hash function
141+
[source, javascript]
142+
----
143+
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=hashFunction, indent=0]
144+
----
145+
146+
Is somewhat similar to what we did before, in the sense that we use each letter’s Unicode is used to compute the hash. The difference is:
147+
148+
1. We are using the XOR bitwise operation (^) to produce an *avalanche effect*, where a small change in two strings produces completely different hash codes. E.g.
149+
150+
.Hash Code example using FVN1a
151+
[source, javascript]
152+
----
153+
hashCode('cat') //↪️ 4201630708
154+
hashCode('cats') //↪️ 3304940933
155+
----
156+
157+
.Fowler/Noll/Vo (FNV) Hash
158+
****
159+
It is a non-cryptographic hash function designed to be fast while maintaining a low collision rate. The high dispersion of the FNV hashes makes them well suited for hashing nearly identical strings such as URLs, keys, IP addresses, zip codes, and others.
160+
****
161+
162+
We are using the FVN-1a prime number (16777619) and offset (2166136261) to reduce collisions even further. If you are curious where these numbers come from check out this https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function[link].
163+
164+
FVN-1a hash function is a good trade-off between speed and collision prevention.
165+
166+
Now that we have a proper hash function. Let’s move on with the rest of the HashMap implementation.
167+
168+
== Implementing a HashMap in JavaScript
169+
170+
Let’s start by creating a class and its constructor to initialize the hash map. We are going to have an array called *buckets* to hold all the data as below:
171+
172+
.HashMap's constructor
173+
[source, javascript]
174+
----
175+
class HashMap {
176+
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=constructorPartial, indent=2]
177+
this.buckets = new Array(this.initialCapacity);
178+
this.size = 0;
179+
this.collisions = 0;
180+
}
181+
182+
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=getLoadFactor, indent=2]
183+
}
184+
----
185+
186+
Notice that we are also keeping track of collisions (just for benchmarking purposes) and a load factor. *The load factor* measures how full the hash map is. We don’t want to be fuller than 75%. If the HashMap is getting too full, then we are going to fix it doing a *rehash* (more on that later).
187+
188+
=== Inserting elements in a HashMap
189+
190+
To insert values into a HashMap, we first convert the *key* into *an array index* using the hash function. Each bucket of the array will have an object `{key, value}`.
191+
192+
There are multiple scenarios for inserting key/values in a HashMap:
193+
194+
1. Key doesn’t exist yet, so we create the new key/value pair.
195+
2. Key already exists, then we will replace the value.
196+
3. Key doesn’t exist, but the bucket already has other data, this is a collision! We push the new element to the bucket.
197+
198+
In code, it looks like this:
199+
200+
.HashMap's set method
201+
[source, javascript]
202+
----
203+
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=set, indent=0]
204+
----
205+
206+
Notice, that we are using a function called `getEntry` to check if the key already exists. It gets the index of the bucket corresponding to the key and then checks if the entry with the given key exists. We are going to implement this function in a bit.
207+
208+
=== Getting values out of a HashMap
209+
210+
For getting values out of the Map, we do something similar to inserting. We convert the key into an index using the hash function.
211+
212+
.HashMap's getEntry method
213+
[source, javascript]
214+
----
215+
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=getEntry, indent=0]
216+
----
217+
<1> Convert key to an array index.
218+
<2> If the bucket is empty create a new linked list
219+
<3> Use Linked list's <<Searching by value>> method to find value on the bucket.
220+
<4> Return bucket and entry if found.
221+
222+
With the help of the `getEntry` method, we can do the `HashMap.get` and `HashMap.has` methods:
223+
224+
.HashMap's get method
225+
[source, javascript]
226+
----
227+
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=get, indent=0]
228+
----
229+
230+
and also,
231+
232+
.HashMap's has method
233+
[source, javascript]
234+
----
235+
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=has, indent=0]
236+
----
237+
238+
For `HashMap.has` we only care if the value exists or not, while that for `HashMap.get` we want to return the value or `undefined` if it doesn’t exist.
239+
240+
=== Deleting from a HashMap
241+
242+
Removing items from a HashMap is not too different from what we did before:
243+
244+
.HashMap's delete method
245+
[source, javascript]
246+
----
247+
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=delete, indent=0]
248+
----
249+
250+
If the bucket doesn’t exist or is empty, we don't have to do anything else. If the value exists, we use the
251+
https://github.com/amejiarosario/dsa.js/blob/master/src/data-structures/linked-lists/linked-list.js[`LinkedList.remove` ]
252+
method.
253+
254+
== Rehashing the HashMap
255+
256+
Rehashing is a technique to minimize collisions when a hash map is getting full. It doubles the size of the map and recomputes all the hash codes and insert data in the new bucket.
257+
258+
When we increase the map size, we try to find the next prime. We explained that keeping the bucket size a prime number is beneficial for minimizing collisions.
259+
260+
.HashMap's rehash method
261+
[source, javascript]
262+
----
263+
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=rehash, indent=0]
264+
----
265+
266+
In the
267+
https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/hash-maps/primes.js[prime.js] file you can find the implementation for finding the next prime. Also, you can see the full HashMap implementation on this file: https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/hash-maps/hashmap.js[hashmap.js]
268+
269+
== HashMap time complexity
270+
271+
Hash Map it’s very optimal for searching values by key in constant time *O(1)*. However, searching by value is not any better than an array since we have to visit every value *O(n)*.
272+
273+
.Time complexity for a Hash Map
274+
|===
275+
.2+.^s| Data Structure 2+^s| Searching By .2+^.^s| Insert .2+^.^s| Delete .2+^.^s| Space Complexity
276+
^|_Index/Key_ ^|_Value_
277+
| Hash Map (naïve) ^|O(n) ^|O(n) ^|O(n) ^|O(n) ^|O(n)
278+
| Hash Map (optimized) ^|O(1)* ^|O(n) ^|O(1)* ^|O(1)* ^|O(1)*
279+
|===
280+
{empty}* = Amortized run time. E.g. rehashing might affect run time.
281+
282+
As you can notice we have amortized times since, in the unfortunate case of a rehash, it will take O(n) while it resizes. After that, it will be on average *O(1)*.

‎book/chapters/map-intro.adoc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
= Map
2+
3+
A map is a data structure to store pairs of data: *key* and *value*. In an array, you can only store values. The array’s key is always the position index. However, in a *Map* the key can be whatever you want.
4+
5+
IMPORTANT: Map is a data structure that _maps_ *keys* to *values*.
6+
7+
Many languages have maps already built-in. JavaScript/Node has `Map`:
8+
9+
.JavaScript Built-in Map Usage
10+
[source, javascript]
11+
----
12+
include::{codedir}/data-structures/maps/map.js[tag=snippet, indent=0]
13+
----
14+
15+
The attractive part of Maps is that they are very performant usually *O(1)* or *O(log n)* depending on the implementation. We can implement the maps using two different techniques:
16+
17+
* *HashMap*: it’s a map implementation using an *array* and *hash function*. The job of the hash function is to convert the key into an index that contains the matching data. Optimized HashMap can have an average runtime of *O(1)*.
18+
* *TreeMap*: it’s a map implementation that uses a self-balanced Binary Search Tree (red-black tree). The BST nodes store the key, and the value and nodes are sorted by key guaranteeing an *O(log n)* look up.

‎book/chapters/map-treemap.adoc

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
= TreeMap
2+
3+
A TreeMap is a Map implementation using Binary Search Trees.
4+
5+
Implementing a Map with a tree, TreeMap, has a couple of advantages over a HashMap:
6+
7+
* Keys are always sorted.
8+
* Statistical data can be easily obtained like the median, highest, lowest key.
9+
* Collisions are not a concern so in the worst case is still *O(log n)*.
10+
* Trees are more space efficient and don’t need to allocate memory beforehand (e.g. `HashMap`’s initial capacity) nor you have to rehash when is getting full.
11+
12+
Ok, now that you know the advantages, let’s implement it!
13+
For a full comparison read the <<HashMap vs TreeMap>> section.
14+
15+
Let’s get started with the essential functions. They have the same interface as the `HashMap` (but the implementation is different).
16+
17+
.TreeMap class overview
18+
[source, javascript]
19+
----
20+
class TreeMap {
21+
constructor(){}
22+
set(key, value) {}
23+
get(key) {}
24+
has(key) {}
25+
delete(key) {}
26+
}
27+
----
28+
29+
== Inserting values into a TreeMap
30+
31+
For inserting a value on a TreeMap, we first need to inialize the tree:
32+
33+
.TreeMap constructor
34+
[source, javascript]
35+
----
36+
include::{codedir}/data-structures/maps/tree-maps/tree-map.js[tag=constructor, indent=0]
37+
----
38+
39+
The tree can be an instance of any Binary Search Tree that we implemented so far. However, for better performance, it should be a self-balanced tree like a https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/trees/red-black-tree.js[Red-Black Tree] or https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/trees/avl-tree.js[AVL Tree].
40+
41+
42+
Let's implement the method to add values to the tree.
43+
44+
.TreeMap `add` method and `size` attribute
45+
[source, javascript]
46+
----
47+
include::{codedir}/data-structures/maps/tree-maps/tree-map.js[tag=set, indent=0]
48+
----
49+
50+
Adding values is very easy (once we have the underlying tree implementation).
51+
52+
== Getting values out of a TreeMap
53+
54+
When We search by key in a tree map, it takes *O(log n)*. This is the implementation:
55+
56+
.TreeMap `get` and `has` method
57+
[source, javascript]
58+
----
59+
include::{codedir}/data-structures/maps/tree-maps/tree-map.js[tag=get, indent=0]
60+
----
61+
62+
One side effect of storing keys in a tree is that they don't come up in insertion order. Instead, they ordered by value.
63+
64+
.TreeMap iterators
65+
[source, javascript]
66+
----
67+
include::{codedir}/data-structures/maps/tree-maps/tree-map.js[tag=iterators, indent=0]
68+
----
69+
<1> We implemented the default iterator using the in-order traversal. That's useful for getting the keys sorted.
70+
71+
.JavaScript Iterators and Generators
72+
****
73+
Generators are useful for producing values that can you can iterate in a `for...of` loop. Generators use the `function*` syntax which expects to have a `yield` with a value.
74+
****
75+
76+
== Deleting values from a TreeMap
77+
78+
Removing elements from TreeMap is simple.
79+
80+
.TreeMap `delete` method
81+
[source, javascript]
82+
----
83+
include::{codedir}/data-structures/maps/tree-maps/tree-map.js[tag=delete, indent=0]
84+
----
85+
86+
The BST implementation does all the heavy lifting.
87+
88+
That’s it! To see the full file in context, click here: https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/maps/tree-maps/tree-map.js[here]

‎book/chapters/map.adoc

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -274,11 +274,13 @@ Removing items from a HashMap is not too different from what we did before:
274274
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=delete, indent=0]
275275
----
276276

277-
If the bucket doesn’t exist or is empty, we don't have to do anything else. If the value exists, we use the linked list `remove` method. If you wonder what
277+
If the bucket doesn’t exist or is empty, we don't have to do anything else. If the value exists, we use the
278+
https://github.com/amejiarosario/dsa.js[`LinkedList.remove` ]
279+
method.
278280

279281
=== Rehashing the HashMap
280282

281-
Rehashing is a technique to minimize collisions. It doubles the size of the map and recomputes all the hash codes and insert data in the new bucket.
283+
Rehashing is a technique to minimize collisions when a hash map is getting full. It doubles the size of the map and recomputes all the hash codes and insert data in the new bucket.
282284

283285
When we increase the map size, we try to find the next prime. We explained that keeping the bucket size a prime number is beneficial for minimizing collisions.
284286

@@ -288,11 +290,12 @@ When we increase the map size, we try to find the next prime. We explained that
288290
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=rehash, indent=0]
289291
----
290292

291-
The algorithms for finding the next prime is implemented https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/hash-maps/primes.js[here] and you can find the full HashMap implementation on this file: https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/hash-maps/hashmap.js
293+
In the
294+
https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/hash-maps/primes.js[prime.js] file you can find the implementation for finding the next prime. Also, you can see the full HashMap implementation on this file: https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/hash-maps/hashmap.js[hashmap.js]
292295

293296
== HashMap time complexity
294297

295-
Hash Map it’s very optimal for searching values by key *O(1)**. However, searching values directly is not any better than an array since we have to visit every value *O(n)*.
298+
Hash Map it’s very optimal for searching values by key in constant time *O(1)*. However, searching by value is not any better than an array since we have to visit every value *O(n)*.
296299

297300
.Time complexity for a Hash Map
298301
|===
@@ -303,23 +306,21 @@ Hash Map it’s very optimal for searching values by key *O(1)**. However, searc
303306
|===
304307
{empty}* = Amortized run time. E.g. rehashing might affect run time.
305308

306-
As you can notice we have amortized times, since in the unfortunate case of a rehash, it will take O(n) while it resizes. After that it will be on average *O(1)*.
307-
308-
The full HashMap implementation with comments can be found on: https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/hash-maps/hashmap.js
309+
As you can notice we have amortized times since, in the unfortunate case of a rehash, it will take O(n) while it resizes. After that, it will be on average *O(1)*.
309310

310311
== Implementing a TreeMap
311312

312313
Implementing a Map with a tree, TreeMap, has a couple of advantages over a HashMap:
313314

314315
* Keys are always sorted.
315-
* Statistical data can be easily obtained like median, highest, lowest key.
316+
* Statistical data can be easily obtained like the median, highest, lowest key.
316317
* Collisions are not a concern so in the worst case is still *O(log n)*.
317-
* Trees are more space efficient and doesn’t need to allocate memory beforehand (e.g. `HashMap`’s initial capacity) nor you have to rehash when is getting full.
318+
* Trees are more space efficient and don’t need to allocate memory beforehand (e.g. `HashMap`’s initial capacity) nor you have to rehash when is getting full.
318319

319320
Ok, now that you know the advantages, let’s implement it!
320321
For a full comparison read the <<HashMap vs TreeMap>> section again.
321322

322-
Let’s get started with the basic functions. They have the same interface as the `HashMap` (but obviously the implementation is different).
323+
Let’s get started with the essential functions. They have the same interface as the `HashMap` (but the implementation is different).
323324

324325
.TreeMap class overview
325326
[source, javascript]
@@ -343,7 +344,7 @@ For inserting a value on a TreeMap, we first need to inialize the tree:
343344
include::{codedir}/data-structures/maps/tree-maps/tree-map.js[tag=constructor, indent=0]
344345
----
345346

346-
The tree, can be an instance of any Binary Search Tree that we implemented so far. However, for better performance it should be a self-balanced tree like a https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/trees/red-black-tree.js[Red-Black Tree] or https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/trees/avl-tree.js[AVL Tree].
347+
The tree can be an instance of any Binary Search Tree that we implemented so far. However, for better performance, it should be a self-balanced tree like a https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/trees/red-black-tree.js[Red-Black Tree] or https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/trees/avl-tree.js[AVL Tree].
347348

348349

349350
Let's implement the method to add values to the tree.
@@ -366,18 +367,18 @@ When We search by key in a tree map, it takes *O(log n)*. This is the implementa
366367
include::{codedir}/data-structures/maps/tree-maps/tree-map.js[tag=get, indent=0]
367368
----
368369

369-
One side effect of storing keys in a tree is that they can be retrieve in order.
370+
One side effect of storing keys in a tree is that they don't come up in insertion order. Instead, they ordered by value.
370371

371372
.TreeMap iterators
372373
[source, javascript]
373374
----
374375
include::{codedir}/data-structures/maps/tree-maps/tree-map.js[tag=iterators, indent=0]
375376
----
376-
<1> We implemented the default iterator using the in-order traveral. That's useful to getting the keys sorted.
377+
<1> We implemented the default iterator using the in-order traversal. That's useful for getting the keys sorted.
377378

378379
.JavaScript Iterators and Generators
379380
****
380-
Generators are useful for producing values that can be iterated in a `for...of` loop. Generators use the `function*` syntax which expects to have a `yield` with a value.
381+
Generators are useful for producing values that can you can iterate in a `for...of` loop. Generators use the `function*` syntax which expects to have a `yield` with a value.
381382
****
382383

383384
=== Deleting values from a TreeMap
@@ -392,12 +393,12 @@ include::{codedir}/data-structures/maps/tree-maps/tree-map.js[tag=delete, indent
392393

393394
The BST implementation does all the heavy lifting.
394395

395-
That’s basically it! To see the full file in context, click here: https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/maps/tree-maps/tree-map.js[https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/maps/tree-maps/tree-map.js]
396+
That’s it! To see the full file in context, click here: https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/maps/tree-maps/tree-map.js[https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/maps/tree-maps/tree-map.js]
396397

397398

398399
== TreeMap Time complexity vs HashMap
399400

400-
As we discussed so far, there are trade-off between the implementations
401+
As we discussed so far, there are trade-offs between the implementations
401402

402403
.Time complexity for different Maps implementations
403404
|===

0 commit comments

Comments
 (0)
Please sign in to comment.