|
1 | 1 | = Set
|
2 | 2 |
|
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. |
0 commit comments