Skip to content

Commit 1991d5d

Browse files
committedOct 16, 2018
finish set docs
1 parent 4abedb3 commit 1991d5d

File tree

5 files changed

+273
-3859
lines changed

5 files changed

+273
-3859
lines changed
 

‎book/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ PDF = --backend=pdf --require=asciidoctor-pdf
1414
EPUB = --backend=epub3 --require=asciidoctor-epub3
1515
KINDLE = ${EPUB} -a ebook-format=kf8
1616

17-
all: html raw_html pdf
17+
all: html
18+
# all: html raw_html pdf
1819
# all: clean manpage html raw_html pdf compressed_pdf epub kindle
1920

2021
manpage:

‎book/chapters/output.adoc

Lines changed: 0 additions & 3846 deletions
Large diffs are not rendered by default.

‎book/chapters/set.adoc

Lines changed: 240 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,242 @@
11
= Set
22

3-
Est ea voluptate consectetur id consequat Lorem non commodo officia. Fugiat quis incididunt pariatur eu Lorem adipisicing nostrud laborum minim dolor labore eu culpa. Ullamco duis officia sint Lorem adipisicing nostrud duis pariatur fugiat commodo nostrud labore.
3+
A set is a data structure where duplicated are not allowed.
4+
5+
NOTE: JavaScript has already a built-in Set data structure.
6+
7+
Take a look at the following
8+
example:
9+
10+
.Set usage example (using JavaScript built-in Set)
11+
[source, javascript]
12+
----
13+
const set = new Set();
14+
15+
set.add(1); //↪️ Set [ 1 ]
16+
set.add(1); //↪️ Set [ 1 ]
17+
set.add(2); //↪️ Set [ 1, 2 ]
18+
set.add(3); //↪️ Set [ 1, 2, 3 ]
19+
set.has(1); //↪️ true
20+
set.delete(1); //↪️ removes 1 from the set
21+
set.has(1); //↪️ false, 1 has been removed
22+
set.size; //↪️ 2, we just removed one value
23+
console.log(set); //↪️ Set(2) {2, 3}
24+
----
25+
26+
As you can see, even if we insert the same value multiple times, it only
27+
gets added once.
28+
29+
Can you think in a way how to implement it?
30+
31+
TIP: A hint... it should perform all operations in *O(1)** or at most *O(log
32+
n)*
33+
34+
If we use a Map’s keys we can accomplish this, right? They cannot be
35+
duplicates and we could just ignore the value part.
36+
37+
== HashSet vs TreeSet
38+
39+
As we saw earlier, we have to ways of implanting maps using a *balanced
40+
BST* and using a *hash function*. If we use them to implement a `Set`
41+
then we would have a `HashSet` and `TreeSet` respectively.
42+
43+
* `TreeSet`, would return the values sorted in ascending order.
44+
* `HashSet`, would return the values in insertion order.
45+
* Operations on a `HashSet` would take on average O(1) and in the worst
46+
case (rehash is due), it would take O(n).
47+
* Operation on a `TreeSet` is always O(log n).
48+
49+
Let’s implement both!
50+
51+
== Implementing a TreeSet
52+
53+
We are to use a balanced BST (Red-Black Tree) to implement TreeSet.
54+
Other self-balanced tree implementations will do.
55+
56+
.TreeSet's constructor method and size attribute
57+
[source, javascript]
58+
----
59+
include::{codedir}/data-structures/sets/tree-set.js[tag=constructor]
60+
}
61+
----
62+
<1> When we create a new Set we can pass a collection of data to be added to the set.
63+
64+
A common use case for Sets is to remove duplicated values from an array. We can do that by passing them in the constructor as follows:
65+
66+
67+
.Removing duplicates from an Array using a Set
68+
[source, javascript]
69+
----
70+
set = new TreeSet([1, 2, 3, 1]);
71+
expect(set.size).toBe(3);
72+
expect(Array.from(set.keys())).toEqual([1, 2, 3]);
73+
----
74+
75+
Ok, now let’s implement the add method.
76+
77+
=== Adding elements to a TreeSet
78+
79+
For adding values to the set, we just pass it to the Tree add method.
80+
81+
.TreeSet's constructor method and size attribute
82+
[source, javascript]
83+
----
84+
include::{codedir}/data-structures/sets/tree-set.js[tag=constructor]
85+
}
86+
----
87+
88+
If you remember, our BST implementation allows duplicated values. It has
89+
a multiplicity tally to keep track of duplicates. However, we don’t want
90+
that in a set, we want each value only once. For that we check if the
91+
value is already in the tree. Don’t worry for adding extra lookups. The
92+
has is also very performant *O(log n)*. Let’s implement it!
93+
94+
=== Searching for values in a TreeSet
95+
96+
Again, we rely on the Tree implementation to do the heavy lifting:
97+
98+
.TreeSet's `has` method
99+
[source, javascript]
100+
----
101+
include::{codedir}/data-structures/sets/tree-set.js[tag=has, indent=0]
102+
----
103+
104+
The get method retrieve the data, but we only need a Boolean.
105+
106+
TIP: We are using the `!!` to convert the result to a Boolean.
107+
108+
=== Converting TreeSet to Array
109+
110+
A common use case to consume the data from a Set is to covert it to an
111+
array or use in an iterator (for loops, forEach, …). Let’s provide the
112+
method for that:
113+
114+
.TreeSet's iterator
115+
[source, javascript]
116+
----
117+
include::{codedir}/data-structures/sets/tree-set.js[tag=iterator, indent=0]
118+
----
119+
120+
We are using the `inOrderTraversal` method of the BST to go each key in an
121+
ascending order.
122+
123+
.JavaScript Built-in `Symbol` iterator
124+
****
125+
The `Symbol.iterator` built-in symbol specifies the default iterator for
126+
an object. Used by `for...of`, `Array.from` and others.
127+
****
128+
129+
Now we can convert from set to array and vice versa easily. For
130+
instance:
131+
132+
.TreeSet's iterator
133+
[source, javascript]
134+
----
135+
const array = [1, 1, 2, 3, 5];
136+
137+
// array to set
138+
const set = new TreeSet(array);
139+
140+
// set to array
141+
Array.from(set); //↪️ (4) [1, 2, 3, 5]
142+
----
143+
144+
No more duplicates in our array!
145+
146+
=== Deleting elements from a TreeSet
147+
148+
We delete the elements from the TreeSet using the remove method of the
149+
BST.
150+
151+
.TreeSet's `delete` method
152+
[source, javascript]
153+
----
154+
include::{codedir}/data-structures/sets/tree-set.js[tag=delete, indent=0]
155+
----
156+
157+
Voilà! That’s it!
158+
159+
Check out the full implementation on here[https://github.com/amejiarosario/algorithms.js/blob/master/src/data-structures/sets/tree-set.js]
160+
161+
Let’s now, implement a HashSet.
162+
163+
== Implementing a HashSet
164+
165+
The *HashSet* is the set implementation using a HashMap as its
166+
underlying data structure.
167+
168+
The HashSet interface will be exactly the same as the built-in `Set` or
169+
our `TreeSet`.
170+
171+
.HashSet's constructor method and size attribute
172+
[source, javascript]
173+
----
174+
include::{codedir}/data-structures/sets/hash-set.js[tag=constructor]
175+
}
176+
----
177+
178+
This constructor is useful for converting an array to set and
179+
initializing the HashMap.
180+
181+
=== Inserting values to a HashSet
182+
183+
To insert items in a HashSet we use the `set` method of the `HashMap`:
184+
185+
.HashSet's `add` method
186+
[source, javascript]
187+
----
188+
include::{codedir}/data-structures/sets/hash-set.js[tag=add, indent=0]
189+
}
190+
----
191+
192+
`HashMap` stores key/value pairs, but for this we only need the key and we
193+
ignore the value.
194+
195+
=== Finding values in a HashSet
196+
197+
We use the method `has` to check if a value is on the `Set` or not.
198+
199+
.HashSet's `has` method
200+
[source, javascript]
201+
----
202+
include::{codedir}/data-structures/sets/hash-set.js[tag=has, indent=0]
203+
----
204+
205+
Internally, the `HashMap` will convert the key into an array index using a hash
206+
function. If there’s something in the array index bucket, it will return
207+
true, if it’s empty it will be false.
208+
209+
=== Deleting values from a HashSet
210+
211+
For deleting a value from a hashSet we use the HashMap’s delete method:
212+
213+
.HashSet's `delete` method
214+
[source, javascript]
215+
----
216+
include::{codedir}/data-structures/sets/hash-set.js[tag=delete, indent=0]
217+
----
218+
219+
220+
This method has an Average runtime of *O(1)*.
221+
222+
== HashSet vs HashMap Time Complexity
223+
224+
We can say that HashMap in on average more performant, however if a
225+
rehash happens it will take *O(n)* instead of *O(1)*. While TreeSet is
226+
always *O(log n)*.
227+
228+
.Time complexity HashSet vs TreeSet
229+
|===
230+
.2+.^s| Data Structure 2+^s| Searching By .2+^.^s| Insert .2+^.^s| Delete .2+^.^s| Space Complexity
231+
^|_Index/Key_ ^|_Value_
232+
| HashSet ^|- ^|O(n) ^|O(1)* ^|O(1)* ^|O(1)*
233+
| TreeSet ^|- ^|O(n) ^|O(log n) ^|O(log n) ^|O(log n)
234+
|===
235+
{empty}* = Amortized run time. E.g. rehashing might affect run time to *O(n)*.
236+
237+
To recap, HashSet and TreeSet will keep data without duplicates. The
238+
difference besides runtime is that:
239+
240+
.TreeSet vs HashSet
241+
* HashSet keeps data in insertion order
242+
* TreeSet keeps data sorted in ascending order.

‎src/data-structures/sets/hash-set.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const HashMap = require('../maps/hash-maps/hash-map');
2-
2+
// tag::constructor[]
33
/**
44
* Set implemented with our HashMap
55
* Have an average of O(1) time on all operations
@@ -15,6 +15,15 @@ class HashMapSet {
1515
Array.from(iterable).forEach(element => this.add(element));
1616
}
1717

18+
/**
19+
* Get size of the set
20+
*/
21+
get size() {
22+
return this.hashMap.size;
23+
}
24+
// end::constructor[]
25+
26+
// tag::add[]
1827
/**
1928
* Add a new value (duplicates will be added only once)
2029
* Avg. Runtime: O(1)
@@ -23,7 +32,9 @@ class HashMapSet {
2332
add(value) {
2433
this.hashMap.set(value);
2534
}
26-
35+
// end::add[]
36+
37+
// tag::has[]
2738
/**
2839
* Check if value is already on the set
2940
* Avg. Runtime: O(1)
@@ -32,14 +43,9 @@ class HashMapSet {
3243
has(value) {
3344
return this.hashMap.has(value);
3445
}
35-
36-
/**
37-
* Get size of the set
38-
*/
39-
get size() {
40-
return this.hashMap.size;
41-
}
42-
46+
// end::has[]
47+
48+
// tag::delete[]
4349
/**
4450
* Delete a value from the set
4551
* Avg. Runtime: O(1)
@@ -48,7 +54,9 @@ class HashMapSet {
4854
delete(value) {
4955
return this.hashMap.delete(value);
5056
}
57+
// end::delete[]
5158

59+
// tag::iterators[]
5260
/**
5361
* Make this class iterable
5462
*/
@@ -75,6 +83,8 @@ class HashMapSet {
7583
yield [value, value];
7684
}
7785
}
86+
// end::iterators[]
87+
7888
}
7989

8090
module.exports = HashMapSet;

‎src/data-structures/sets/tree-set.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// const Tree = require('../trees/avl-tree'); // faster lookups
22
const Tree = require('../trees/red-black-tree'); // faster insertion
33

4+
// tag::constructor[]
45
/**
56
* TreeSet implements a Set (collection of unique values)
67
* using a balanced binary search tree to guarantee a O(log n) in all operations.
@@ -12,7 +13,7 @@ class TreeSet {
1213
*/
1314
constructor(iterable = []) {
1415
this.tree = new Tree();
15-
Array.from(iterable).forEach(value => this.add(value));
16+
Array.from(iterable).forEach(value => this.add(value)); // <1>
1617
}
1718

1819
/**
@@ -21,7 +22,9 @@ class TreeSet {
2122
get size() {
2223
return this.tree.size;
2324
}
25+
// end::constructor[]
2426

27+
// tag::constructor[]
2528
/**
2629
* Add a new value (duplicates will be added only once)
2730
* Runtime: O(log n)
@@ -32,7 +35,9 @@ class TreeSet {
3235
this.tree.add(value);
3336
}
3437
}
38+
// end::constructor[]
3539

40+
// tag::has[]
3641
/**
3742
* Check if value is already on the set
3843
* Runtime: O(log n)
@@ -42,7 +47,9 @@ class TreeSet {
4247
has(value) {
4348
return this.tree.has(value);
4449
}
50+
// end::has[]
4551

52+
// tag::delete[]
4653
/**
4754
* Delete a value from the set
4855
* Runtime: O(log n)
@@ -51,7 +58,9 @@ class TreeSet {
5158
delete(value) {
5259
return this.tree.remove(value);
5360
}
61+
// end::delete[]
5462

63+
// tag::iterator[]
5564
/**
5665
* Default iterator for this set
5766
* @returns {iterator} values in ascending order
@@ -61,6 +70,7 @@ class TreeSet {
6170
yield node.value;
6271
}
6372
}
73+
// end::iterator[]
6474

6575
/**
6676
* Get all the values on the Set

0 commit comments

Comments
 (0)
Please sign in to comment.