Skip to content

Commit d18186b

Browse files
committedSep 3, 2020
feat(book/hashmap): add exercises and solutions
1 parent 1bfe522 commit d18186b

8 files changed

+421
-33
lines changed
 

‎book/D-interview-questions-solutions.asc

Lines changed: 163 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,11 @@ If the index overflows, it moves to the next node and reset the index to zero.
196196
[#stack-q-valid-parentheses]
197197
include::content/part02/stack.asc[tag=stack-q-valid-parentheses]
198198

199-
.We need to validate that brackets are properly opened and closed, following these rules:
199+
.We need to validate that brackets are correctly opened and closed, following these rules:
200200
- An opened bracket must be close by the same type.
201201
- Open brackets mush be closed in the correct order.
202202

203-
This is a parsing problem, and usually, stacks are good candidates for them.
203+
We are facing a parsing problem, and usually, stacks are good candidates for them.
204204

205205
*Algorithm*:
206206

@@ -391,8 +391,8 @@ We can visit the tree using a Queue and keep track when a level ends, and the ne
391391

392392
Since during BFS, we dequeue one node and enqueue their two children (left and right), we might have two levels (current and next one). For this problem, we need to know what the last node on the current level is.
393393

394-
.There are several ways to solve this problem using BFS. Here are some ideas:
395-
- *1 Queue + Sentinel node*: we can use a special character in the `Queue` like `'*'` or `null` to indicate the level's change. So, we would start something like this `const queue = new Queue([root, '*']);`.
394+
.There are several ways to solve this problem by using BFS. Here are some ideas:
395+
- *1 Queue + Sentinel node*: we can use a special character in the `Queue` like `'*'` or `null` to indicate a level change. So, we would start something like this `const queue = new Queue([root, '*']);`.
396396
- *2 Queues*: using a "special" character might be seen as hacky, so you can also opt to keep two queues: one for the current level and another for the next level.
397397
- *1 Queue + size tracking*: we track the Queue's `size` before the children are enqueued. That way, we know where the current level ends.
398398

@@ -428,8 +428,164 @@ The complexity of any of the BFS methods or DFS is similar.
428428
- Space: `O(n)`. For BFS, the worst-case space is given by the maximum *width*. That is when the binary tree is complete so that the last level would have `(n-1)/2` nodes, thus `O(n)`. For the DFS, the space complexity will be given by the tree's maximum *height*. In the worst-case, the binary tree is skewed to the right so that we will have an implicit call stack of size `n`.
429429

430430

431-
// [#linkedlist-q-FILENAME]
432-
// include::content/part02/linked-list.asc[tag=linkedlist-q-FILENAME]
431+
432+
:leveloffset: +1
433+
434+
=== Solutions for Hash Map Questions
435+
(((Interview Questions Solutions, Hash Map)))
436+
437+
:leveloffset: -1
438+
439+
[#hashmap-q-two-sum]
440+
include::content/part03/hashmap.asc[tag=hashmap-q-two-sum]
441+
442+
This simple problem can have many solutions; let's explore some.
443+
444+
_Brute force_
445+
446+
One brute force approach could be doing two for loops. We sum two different numbers and check if they add up to the target. If yes, we return, and if not, we keep increasing the indices until we check every possible pair.
447+
448+
[source, javascript]
449+
----
450+
include::interview-questions/two-sum.js[tags=twoSumBrute]
451+
----
452+
453+
This approach's time complexity is `O(n^2)`, because we visit every number twice in the worst-case. While the space complexity is `O(1)`.
454+
455+
Can we trade space for time? Yes!
456+
457+
_Map_
458+
459+
Based on `nums[i] + nums[j] === target` we can say that `num[j] === target - nums[i]`. We can do one pass and check if we have seen any number equal to `target - nums[i]`. A map is perfect for this job. We could have a HashMap that maps `num` to `index`. Let's see the algorithms to make it work.
460+
461+
462+
*Algorithm*:
463+
464+
* Visit every number once
465+
** Calculate the complement `target - nums[i]`.
466+
** If the complement exists, return its index and the current index.
467+
** If not, save the complement and the index number.
468+
469+
*Implementation*:
470+
471+
[source, javascript]
472+
----
473+
include::interview-questions/two-sum.js[tags=description;solution]
474+
----
475+
476+
*Complexity Analysis*:
477+
478+
- Time: `O(n)`. We visit every number once.
479+
- Space: `O(n)`. In the worst-case scenario, we don't find the target, and we ended up with a map with all the numbers from the array.
480+
481+
482+
[#hashmap-q-subarray-sum-equals-k]
483+
include::content/part03/hashmap.asc[tag=hashmap-q-subarray-sum-equals-k]
484+
485+
This problem has multiple ways to solve it. Let's explore some.
486+
487+
_Brute force_
488+
489+
The most straightforward one is to convert the requirements into code:
490+
generate all possible subarrays, add them up, and check how many are equal to k.
491+
492+
[source, javascript]
493+
----
494+
include::interview-questions/subarray-sum-equals-k.js[tags=subarraySumBrute1]
495+
----
496+
497+
This solution's time complexity is `O(n^3)` because of the 3 nested loops.
498+
499+
How can we do better? Notice that the last for loop, compute the sum repeatedly just to add one more.
500+
Let's fix that!
501+
502+
_Cummulative Sum_
503+
504+
For this solution, instead of computing the sum from `i` to `j` all the time. We can calculate a cumulative sum. Every time we see a new number, we add it to the aggregate.
505+
506+
Since we want all possible subarray, We can increase `i` and get sum for each:
507+
508+
[source, javascript]
509+
----
510+
array = [1, 2, 3, 0, 1, 4, 0, 5];
511+
512+
// cummulative sum from left to right with i = 0
513+
sum = [1, 3, 6, 6, 7, 11, 11, 16];
514+
// cummulative sum from left to right with i = 1
515+
sum = [2, 5, 5, 6, 10, 10, 15];
516+
// cummulative sum from left to right with i = 2
517+
sum = [3, 3, 4, 8, 8, 13];
518+
// ... and so on ...
519+
// cummulative sum from left to right with i = 7
520+
sum = [5];
521+
----
522+
523+
Here's the code:
524+
525+
[source, javascript]
526+
----
527+
include::interview-questions/subarray-sum-equals-k.js[tags=subarraySumBrute1]
528+
----
529+
530+
The time complexity for this solution is better, `O(n^2)`. Can we still do better?
531+
532+
_Map_
533+
534+
Let's get the intution from our previous cummulative sum:
535+
536+
[source, javascript]
537+
----
538+
subarraySum([1, 2, 3, 0, 1, 4, 0, 5], 5); // k = 5
539+
540+
// cummulative sum from left to right is
541+
sum = [1, 3, 6, 6, 7, 11, 11, 16];
542+
// ^ ^
543+
----
544+
545+
Notice that when the array has a 0, the cumulative sum has a repeated number. If you subtract those numbers, it will give you zero. In the same way, If you take two other ranges and subtract them (`sum[j] - sum[i]`), it will give you the sum of that range `sum(num[i]...num[j])`.
546+
547+
For instance, if we take the index `2` and `0` (with values 6 and 1) and susbtract them we get `6-1=5`. To verify we can add the array values from index 0 to 2, `sum([1, 2, 3]) === 5`.
548+
549+
With that intuition, we can use a Map to keep track of the aggregated sum and the number of times that sum.
550+
551+
*Algorithm*:
552+
553+
* Start sum at 0
554+
* Visit every number on the array
555+
** Compute the cumulative sum
556+
** Check if `sum - k` exits; if so, it means that there's a subarray that adds up to k.
557+
** Save the sum and the number of times that it has occurred.
558+
559+
*Implementation*:
560+
561+
[source, javascript]
562+
----
563+
include::interview-questions/subarray-sum-equals-k.js[tags=description;solution]
564+
----
565+
566+
You might wonder, what the map is initialized with `[0, 1]`. Consider this test case:
567+
568+
[source, javascript]
569+
----
570+
subarraySum([1], 1); // k = 1
571+
----
572+
573+
The sum is 1, however `sum - k` is `0`. If it doesn't exist on the map, we will get the wrong answer since that number adds up to `k`. We need to add an initial case on the map: `map.set(0, 1)`. If `nums[i] - k = 0`, then that means that `nums[i] = k` and should be part of the solution.
574+
575+
*Complexity Analysis*:
576+
577+
- Time: `O(n)`. We visit every number once.
578+
- Space: `O(n)`. The map size will be the same as the original array.
579+
580+
// :leveloffset: +1
581+
582+
// === Solutions for TOPIC Questions
583+
// (((Interview Questions Solutions, TOPIC)))
584+
585+
// :leveloffset: -1
586+
587+
// [#TOPIC-q-FILENAME]
588+
// include::content/part03/TOPIC_FILE.asc[tag=TOPIC-q-FILENAME]
433589

434590
// RESTATE REQUIREMENTS AND DESCRIPTIONS
435591

@@ -444,8 +600,7 @@ The complexity of any of the BFS methods or DFS is similar.
444600

445601
// [source, javascript]
446602
// ----
447-
// include::interview-questions/FILENAME.js[tag=description]
448-
// include::interview-questions/FILENAME.js[tag=solution]
603+
// include::interview-questions/FILENAME.js[tags=description;solution]
449604
// ----
450605

451606
// IMPLEMENTATION NOTES

‎book/content/part03/hashmap.asc

Lines changed: 95 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ A HashMap is a Map implementation. HashMaps are composed of two things:
1212
1) a _hash function_ and
1313
2) a bucket _array_ to store values.
1414

15-
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 and animals:
15+
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 and animals:
1616

1717
.HashMap example
1818
[source, javascript]
@@ -31,31 +31,31 @@ image::image41.png[image,width=528,height=299]
3131
1. We use a *hash function* to transform the keys (e.g., dog, cat, rat, …) into an array index. This _array_ is called *bucket*.
3232
2. The bucket holds the values or list of values in case of collisions.
3333

34-
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.
34+
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 though their hash codes are different.
3535

3636
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)*.
3737

38-
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 auto-resizing avoids collisions and don't need to allocate too much memory upfront. Let’s start with the *hash function*.
38+
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 resizes itself when it is getting full. This auto-resizing avoids collisions and don't need to allocate too much memory upfront. Let’s start with the *hash function*.
3939

4040
===== Designing an optimized hash function
4141

4242
To minimize collisions, we need to create an excellent hash function.
4343

4444
IMPORTANT: A *perfect* hash function is one that assigns a unique array index for every different key.
4545

46-
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.
46+
It’s no practical and memory-wise wasteful to have a perfect hash function, so we will shoot for a cost-effective hash function instead.
4747

4848
.To recap:
4949
- A hash function converts keys into array indices.
5050
- A hash function is composed of two parts:
5151
1. *Hash Code*: maps any key into an integer (unbonded)
5252
2. *Compression function*: maps an arbitrary integer to integer in the range of [0… BUCKET_SIZE -1].
5353

54-
Before doing a great hash function, let's see what a lousy hash function looks like. 😉
54+
Before doing an excellent hash function, let's see what a lousy hash function looks like. 😉
5555

5656
====== Analysing collisions on bad hash code functions
5757

58-
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.
58+
A hash code function's goal is to convert any value given into a positive integer — a common way to accomplish with summing each string’s Unicode value.
5959

6060
.Naïve hashing function implementation
6161
[source, javascript]
@@ -72,7 +72,7 @@ The `charCodeAt()` method returns an integer between `0` and `65535` representin
7272
7373
The `codePointAt()` method returns a non-negative integer that is the Unicode code point value.
7474
****
75-
With this function we have the can convert some keys to numbers as follows:
75+
With this function, we have the can convert some keys to numbers as follows:
7676

7777
.Hashing examples
7878
[source, javascript]
@@ -82,7 +82,7 @@ include::{codedir}/data-structures/maps/hash-maps/hashing.js[tag=naiveHashCodeEx
8282

8383
Notice that `rat` and `art` have the same hash code! These are collisions that we need to solve.
8484

85-
Collisions happened because we are adding the letter's Unicode 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'`.
85+
Collisions happened because we add the letter's Unicode and are not taking the order into account or 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 the string `'10'`.
8686

8787
.Hashing function implementation that offset character value based on the position
8888
[source, javascript]
@@ -109,17 +109,17 @@ BigInt has no virtual limits (until you run out of physical memory). It uses the
109109
----
110110
****
111111

112-
As you can imagine, summing 20bits per letter leads to a humongous number! That's the case even for three letters words. We are using `BigInt`, so it doesn’t overflow.
112+
As you can imagine, summing 20bits per letter leads to a massive number! That's the case even for three-letter words. We are using `BigInt`, so it doesn’t overflow.
113113

114114
.Verifying there's not hashing code duplicates
115115
[source, javascript]
116116
----
117117
include::{codedir}/data-structures/maps/hash-maps/hashing.js[tag=hashCodeOffsetExample, indent=0]
118118
----
119119

120-
We don’t have duplicates anymore! If the keys have different content or type, they have a different hash code. However, we need to represent these unbounded integers to finite buckets in an array. We do that using *compression function*. This function can be as simple as `% BUCKET_SIZE`.
120+
We don’t have duplicates anymore! If the keys have different content or type, they have distinct hash codes. However, we need to represent these unbounded integers to finite buckets in an array. We do that using *compression function*. This function can be as simple as `% BUCKET_SIZE`.
121121

122-
However, there’s an issue with the last implementation. It doesn’t matter how enormous (and different) is the hash code 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.
122+
However, there’s an issue with the last implementation. It doesn’t matter how enormous (and different) is the hash code number if we, in the end, use the modulus to get an array index. The part of the hash code that truly matters is the last bits.
123123

124124
.Look at this example with a bucket size of 4.
125125
[source, javascript]
@@ -131,9 +131,9 @@ However, there’s an issue with the last implementation. It doesn’t matter ho
131131
50 % 4 //↪️ 2
132132
----
133133

134-
All the hash codes are different and still we get many collisions! [big]#😱#
134+
All the hash codes are different, and still, we get many collisions! [big]#😱#
135135

136-
Based on numbers properties, using a prime number as the modulus produce fewer collisions.
136+
Based on numbers properties, using a prime number as the modulus produces fewer collisions.
137137

138138
.Let’s see what happens if the bucket size is a prime number:
139139
[source, javascript]
@@ -149,7 +149,7 @@ Now it’s more evenly distributed!! [big]#😎👍#
149149

150150
.So, to sum up:
151151
* Bucket size should always be a *prime number*, so data is distributed more evenly and minimized collisions.
152-
* Hash code doesn’t have to be too big. At the end what matters is the few last digits.
152+
* Hash code doesn’t have to be too big. In the end, what matters is the few last digits.
153153

154154
Let’s design a better HashMap with what we learned.
155155

@@ -171,9 +171,9 @@ Take a look at the following function:
171171
include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=hashFunction, indent=0]
172172
----
173173

174-
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:
174+
It is somewhat similar to what we did before, in the sense that we use each letter’s Unicode to compute the hash. The difference is:
175175

176-
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.
176+
1. We are using the XOR bitwise operation (`^`) to produce an *avalanche effect*, where a small change in two strings makes completely different hash codes. E.g.
177177

178178
.Hash Code example using FVN1a
179179
[source, javascript]
@@ -182,9 +182,9 @@ hashCode('cat') //↪️ 4201630708
182182
hashCode('cats') //↪️ 3304940933
183183
----
184184

185-
A one letter change produce a very different output.
185+
A one-letter change produces a very different output.
186186

187-
We are using the FVN-1a prime number (`16777619`) and the offset (`2166136261`) to reduce collisions even further. If you are curious where these numbers come from check out this http://bit.ly/fvn-1a[link].
187+
We are using the FVN-1a prime number (`16777619`) and the offset (`2166136261`) to reduce collisions even further if you are curious where these numbers come from, check out this http://bit.ly/fvn-1a[link].
188188

189189
FVN-1a hash function is a good trade-off between speed and collision prevention.
190190

@@ -208,7 +208,7 @@ include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=getLoadFactor,
208208
}
209209
----
210210

211-
Notice that we are also keeping track of collisions (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).
211+
Notice that we are also keeping track of collisions (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, we will fix it doing a *rehash* (more on that later).
212212

213213
====== Inserting elements in a HashMap
214214

@@ -225,9 +225,9 @@ include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=set, indent=0]
225225
<1> Key doesn’t exist yet, so we create the new key/value pair.
226226
<2> Key already exists, then we will replace the value.
227227
<3> Key doesn’t exist, but the bucket already has other data, this is a collision! We push the new element to the bucket.
228-
<4> To keep insertion order, we keep track of the order of the keys using `keysTrackerArray` and `keysTrackerIndex`.
228+
<4> To keep insertion order, we keep track of the keys' order using `keysTrackerArray` and `keysTrackerIndex`.
229229

230-
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.
230+
Notice that we are using a function called `getEntry` to check if the key already exists. It gets the bucket's index corresponding to the access and then checks if the entry with the given key exists. We are going to implement this function in a bit.
231231

232232
====== Getting values out of a HashMap
233233

@@ -243,7 +243,7 @@ include::{codedir}/data-structures/maps/hash-maps/hash-map.js[tag=getEntry, inde
243243
<3> Use Linked list's <<part02-linear-data-structures#array-search-by-value>> method to find value on the bucket.
244244
<4> Return `bucket` and `entry` if found.
245245

246-
With the help of the `getEntry` method, we can do the `HashMap.get` and `HashMap.has` methods:
246+
With the `getEntry` method, we can do the `HashMap.get` and `HashMap.has` methods:
247247

248248
.HashMap's get method
249249
[source, javascript]
@@ -277,7 +277,7 @@ method.
277277

278278
===== Rehashing a HashMap
279279

280-
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 buckets.
280+
Rehashing is a technique to minimize collisions when a hash map is getting full. It doubles the map's size and recomputes all the hash codes, and inserts data in the new buckets.
281281

282282
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.
283283

@@ -292,7 +292,7 @@ https://github.com/amejiarosario/dsa.js/blob/7694c20d13f6c53457ee24fbdfd3c0ac571
292292

293293
===== HashMap time complexity
294294

295-
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)*.
295+
Hash Map is 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)*.
296296
(((Tables, Non-Linear DS, HashMap complexities)))
297297

298298
// tag::table[]
@@ -307,4 +307,74 @@ Hash Map it’s very optimal for searching values by key in constant time *O(1)*
307307
// end::table[]
308308

309309
indexterm:[Runtime, Linear]
310-
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 *O(1)*.
310+
As you can notice, we have amortized times since it will take O(n) while it resizes in the unfortunate case of a rehash. After that, it will be *O(1)*.
311+
312+
313+
==== Practice Questions
314+
(((Interview Questions, Hash Map)))
315+
316+
317+
318+
// tag::hashmap-q-two-sum[]
319+
===== Fit 2 movies in a flight
320+
321+
*HM-1*) _You are working in an entertainment recommendation system for an airline. Given a flight duration (target) and an array of movies length, you need to recommend two movies that fit exactly the length of the flight. Return an array with the indices of the two numbers that add up to the target. No duplicates are allowed. If it's not possible to return empty `[]`._
322+
323+
// end::hashmap-q-two-sum[]
324+
325+
// _Seen in interviews at: Amazon, Google, Apple._
326+
327+
Examples:
328+
329+
[source, javascript]
330+
----
331+
twoSum([113, 248, 80, 200, 91, 201, 68], 316); // [1, 6] (248 + 68 = 316)
332+
twoSum([150, 100, 200], 300); // [2, 3] (100 + 200 = 300)
333+
twoSum([150, 100, 200], 150); // [] (No two numbers add up to 150)
334+
----
335+
336+
Starter code:
337+
338+
[source, javascript]
339+
----
340+
include::../../interview-questions/two-sum.js[tags=description;placeholder]
341+
----
342+
343+
344+
_Solution: <<hashmap-q-two-sum>>_
345+
346+
347+
348+
349+
350+
// tag::hashmap-q-subarray-sum-equals-k[]
351+
===== Subarray Sum that Equals K
352+
353+
*HM-2*) _Given an array of integers, find all the possible subarrays to add up to k. Return the count._
354+
355+
// end::hashmap-q-subarray-sum-equals-k[]
356+
357+
// _Seen in interviews at: Facebook, Google, Amazon_
358+
359+
Examples:
360+
361+
[source, javascript]
362+
----
363+
subarraySum([1], 1); // 1 (1 equals to 1 :)
364+
subarraySum([1, 1, 1], 1); // 3 ([1], [1], [1] equals 1)
365+
subarraySum([1, -1, 1], 0); // 2 (sum([1, -1]), sum([-1, 1]) equals 0)
366+
subaraySum([1, 2, 3, 0, 1, 4, 0, 5], 5) // 8
367+
// All of these 8 sub arrays add up to 5:
368+
// [2, 30], [2,3,0], [0,1,4], [0,1,4,0], [1,4], [1,4,0], [0,5], [5]
369+
----
370+
371+
Starter code:
372+
373+
[source, javascript]
374+
----
375+
include::../../interview-questions/subarray-sum-equals-k.js[tags=description;placeholder]
376+
----
377+
378+
379+
_Solution: <<hashmap-q-subarray-sum-equals-k>>_
380+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// https://leetcode.com/problems/longest-substring-without-repeating-characters/submissions/
2+
3+
function lengthOfLongestSubstring(s: string): number {
4+
let max = 0;
5+
const set = new Set();
6+
7+
for (let i = 0, j = 0; j < s.length; j++) {
8+
while (set.has(s[j])) set.delete(s[i++]);
9+
set.add(s[j]);
10+
max = Math.max(max, set.size);
11+
}
12+
13+
return max;
14+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
describe('', () => {
2+
it('', () => {
3+
4+
});
5+
});
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// tag::description[]
2+
function subarraySum(nums, k) {
3+
// end::description[]
4+
// tag::placeholder[]
5+
// write your code here...
6+
// end::placeholder[]
7+
// tag::solution[]
8+
let ans = 0;
9+
let sum = 0;
10+
const map = new Map([[0, 1]]);
11+
12+
for (let i = 0; i < nums.length; i++) {
13+
sum += nums[i];
14+
if (map.has(sum - k)) ans += map.get(sum - k);
15+
map.set(sum, 1 + (map.get(sum) || 0));
16+
}
17+
18+
return ans;
19+
// end::solution[]
20+
// tag::description[]
21+
}
22+
// end::description[]
23+
24+
// tag::subarraySumBrute1[]
25+
function subarraySumBrute1(nums, k) {
26+
let ans = 0;
27+
28+
for (let i = 0; i < nums.length; i++) {
29+
for (let j = i; j < nums.length; j++) {
30+
let sum = 0;
31+
for (let n = i; n <= j; n++) {
32+
sum += nums[n];
33+
}
34+
if (sum === k) ans++;
35+
}
36+
}
37+
38+
return ans;
39+
}
40+
// end::subarraySumBrute1[]
41+
42+
// tag::subarraySumBrute2[]
43+
function subarraySumBrute2(nums, k) {
44+
let ans = 0;
45+
46+
for (let i = 0; i < nums.length; i++) {
47+
let sum = 0;
48+
for (let j = i; j < nums.length; j++) {
49+
sum += nums[j];
50+
if (sum === k) ans++;
51+
}
52+
}
53+
54+
return ans;
55+
}
56+
// end::subarraySumBrute2[]
57+
58+
module.exports = { subarraySum, subarraySumBrute1, subarraySumBrute2 };
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const { subarraySum, subarraySumBrute1, subarraySumBrute2 } = require('./subarray-sum-equals-k');
2+
// const { } = require('../../src/index');
3+
4+
[subarraySum, subarraySumBrute1, subarraySumBrute2].forEach((fn) => {
5+
describe(`HashMap: ${fn.name}`, () => {
6+
it('should work with null/empty', () => {
7+
const actual = [];
8+
const expected = 0;
9+
expect(fn(actual, 0)).toEqual(expected);
10+
});
11+
12+
it('should work with small case', () => {
13+
const actual = [1, -1, 1];
14+
const expected = 3;
15+
expect(fn(actual, 1)).toEqual(expected);
16+
});
17+
18+
it('should work with other case', () => {
19+
const actual = [1, 2, 3, 0, 1, 4, 0, 5];
20+
const expected = 8;
21+
expect(fn(actual, 5)).toEqual(expected);
22+
});
23+
});
24+
});

‎book/interview-questions/two-sum.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// tag::description[]
2+
function twoSum(nums, target) {
3+
// end::description[]
4+
// tag::placeholder[]
5+
// write your code here...
6+
// end::placeholder[]
7+
// tag::solution[]
8+
const map = new Map();
9+
10+
for (let i = 0; i < nums.length; i++) {
11+
const complement = target - nums[i];
12+
if (map.has(nums[i])) return [map.get(nums[i]), i];
13+
map.set(complement, i);
14+
}
15+
16+
return [];
17+
// end::solution[]
18+
// tag::description[]
19+
}
20+
// end::description[]
21+
22+
// tag::twoSumBrute[]
23+
function twoSumBrute(nums, target) {
24+
for (let i = 0; i < nums.length; i++) {
25+
for (let j = i + 1; j < nums.length; j++) {
26+
if (nums[i] + nums[j] === target) return [i, j];
27+
}
28+
}
29+
30+
return [];
31+
}
32+
// end::twoSumBrute[]
33+
34+
module.exports = { twoSum, twoSumBrute };
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const { twoSum } = require('./two-sum');
2+
// const { } = require('../../src/index');
3+
4+
describe('HashMap: Two Sum', () => {
5+
it('should work with null/empty', () => {
6+
const actual = [];
7+
const expected = [];
8+
expect(twoSum(actual, 0)).toEqual(expected);
9+
});
10+
11+
it('should work with small case', () => {
12+
const actual = [150, 100, 200];
13+
const expected = [1, 2];
14+
expect(twoSum(actual, 300)).toEqual(expected);
15+
});
16+
17+
it('should work with small invalid case', () => {
18+
const actual = [150, 100, 200];
19+
const expected = [];
20+
expect(twoSum(actual, 150)).toEqual(expected);
21+
});
22+
23+
it('should work with other case', () => {
24+
const actual = [113, 248, 80, 200, 91, 201, 68];
25+
const expected = [1, 6];
26+
expect(twoSum(actual, 316)).toEqual(expected);
27+
});
28+
});

0 commit comments

Comments
 (0)
Please sign in to comment.