Skip to content

feat(book/set): add questions and solutions #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 67 additions & 21 deletions book/D-interview-questions-solutions.asc
Original file line number Diff line number Diff line change
Expand Up @@ -577,35 +577,81 @@ The sum is 1, however `sum - k` is `0`. If it doesn't exist on the map, we will
- Time: `O(n)`. We visit every number once.
- Space: `O(n)`. The map size will be the same as the original array.

// :leveloffset: +1

// === Solutions for TOPIC Questions
// (((Interview Questions Solutions, TOPIC)))

// :leveloffset: -1
:leveloffset: +1

=== Solutions for Set Questions
(((Interview Questions Solutions, Set)))

:leveloffset: -1


[#set-q-most-common-word]
include::content/part03/set.asc[tag=set-q-most-common-word]

This problem requires multiple steps. We can use a `Set` for quickly looking up banned words. For getting the count of each word, we used a `Map`.

*Algorithm*:

- Convert text to lowercase.
- Remove any special characters `!?',;.`.
- Convert the paragraph into words array.
- Count how many times words occur.
- Exclude banned words from counts.
- Return the word (or first one) that is the most repeated.

*Implementation*:

[source, javascript]
----
include::interview-questions/most-common-word.js[tags=description;solution]
----

Here are heavily relying on Regular Expressions:

- `\W+` would match all non-words.
- `\s+` catches all whitespace.

The line that is mapping words to count seems very busy. Here's another version of the same code a little bit more explicit:

[source, javascript]
----
include::interview-questions/most-common-word.js[tags=explicit]
----

*Complexity Analysis*:

- Time: `O(m + n)`, where `n` is paragraph length and `m` is the number of banned words. If we were NOT using a `Set` for prohibited words, then the runtime would have been `O(mn)`.
- Space: `O(m + n)`. The extra space complexity is given by the size of the `Map` and `Set`.


// [#TOPIC-q-FILENAME]
// include::content/part03/TOPIC_FILE.asc[tag=TOPIC-q-FILENAME]

// RESTATE REQUIREMENTS AND DESCRIPTIONS

// *Algorithm*:

// - STEP 1
// - STEP 2
// - STEP 2.1
// - STEP 2.2
[#set-q-longest-substring-without-repeating-characters]
include::content/part03/set.asc[tag=set-q-longest-substring-without-repeating-characters]

// *Implementation*:
One of the most efficient ways to find repeating characters is using a `Map` or `Set`. Use a `Map` when you need to keep track of the count/index (e.g., string -> count) and use a `Set` when you only need to know if there are repeated characters or not.

// [source, javascript]
// ----
// include::interview-questions/FILENAME.js[tags=description;solution]
// ----
*Algorithm*:

* Visit each letter.
** Insert the letter on a Set.
** Keep track of the maximum size of the Set in `max`.
** If the letter has been seen before, delete until there's no duplicate.
* Return max.

// IMPLEMENTATION NOTES
*Implementation*:

// *Complexity Analysis*:
[source, javascript]
----
include::interview-questions/longest-substring-without-repeating-characters.js[tags=description;solution]
----

We could also have used a Map and keep track of the indexes, but that's not necessary. In this case, the `Set` is all we need.

*Complexity Analysis*:

// - Time: `O(?)`. WHY?
// - Space: `O(?)`. WHY?
- Time: `O(n)`. We visit each letter once.
- Space: `O(W)`, where `W` is the max length of non-repeating characters. The maximum size of the Set gives the space complexity. In the worst-case scenario, all letters are unique (`W = n`), so our space complexity would be `O(n)`. In the avg. case where there are one or more duplicates, it uses less space than `n`, because `W < n`.
101 changes: 89 additions & 12 deletions book/content/part03/set.asc
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ endif::[]
=== Set
(((Set)))
(((Data Structures, Non-Linear, Set)))
A set is a data structure where duplicated entries are not allowed. Set is like an array with unique values.
A set is a data structure where duplicated entries are not allowed. A Set is like an array with only unique values.

NOTE: JavaScript has already a built-in Set data structure.
NOTE: JavaScript already has a built-in Set data structure.

Take a look at the following
example:
Expand Down Expand Up @@ -38,15 +38,15 @@ TIP: A hint... it should perform all operations in *O(1)** or at most *O(log n)*

If we use a `map`, we can accomplish this. However, maps use a key/value pair. If we only use the keys, we can avoid duplicates. Since in a `map` you can only have one key at a time.

As you might remember from the <<part03-graph-data-structures#map>> chapter, there are two ways of implementing a `map` and both can be used to create a `set`. Let's explore the difference between the two implementations are.
As you might remember from the <<part03-graph-data-structures#map>> chapter, there are two ways of implementing a `map`, and both can be used to create a `set`. Let's explore the difference between the two implementations are.

==== HashSet vs TreeSet

We can implement a `map` using a *balanced BST* and using a *hash function*. If we use them to implement a `Set`, then we would have a `HashSet` and `TreeSet` respectively.
We can implement a `map` using a *balanced BST* or a *hash function*. If we use them to implement a `Set`, we would have a `HashSet` and `TreeSet`.

* `TreeSet`, would return the values sorted in ascending order.
* `HashSet`, would return the values in insertion order.
* Operations on a `HashSet` would take on average O(1) and in the worst case (rehash is due), it would take O(n).
* Operations on a `HashSet` would take on average O(1), and in the worst case (rehash is due), it would take O(n).
* Operation on a `TreeSet` is always O(log n).

Let’s implement both!
Expand All @@ -65,7 +65,7 @@ include::{codedir}/data-structures/sets/tree-set.js[tag=constructor]
----
<1> Converts an array or any iterable data structure to a set.

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:
An everyday use case for Sets is to remove duplicated values from an array. We can do that bypassing them in the constructor as follows:

.Removing duplicates from an Array using a Set
[source, javascript]
Expand Down Expand Up @@ -115,7 +115,7 @@ Voilà! That’s it!

===== Converting TreeSet to Array

A common use case for a Set is to convert it to an array or use in an iterator (for loops, forEach, …). Let’s provide the method for that:
Another use case for a Set is to convert it to an array or use an iterator (for loops, forEach, …). Let’s provide the method for that:

.TreeSet's iterator
[source, javascript]
Expand Down Expand Up @@ -151,7 +151,7 @@ No more duplicates in our array!

Check out our https://github.com/amejiarosario/dsa.js/blob/f69b744a1bddd3d99243ca64b3ad46f3f2dd7342/src/data-structures/sets/tree-set.js#L12[GitHub repo for the full TreeSet implementation].

Let’s now, implement a `HashSet`.
Let’s now implement a `HashSet`.

[[hashset]]
==== HashSet
Expand All @@ -172,7 +172,7 @@ This constructor is useful for converting an array to set and initializing the `

===== Inserting values to a HashSet

To insert items in a HashSet we use the `set` method of the `HashMap`:
To insert items in a HashSet, we use the `set` method of the `HashMap`:

.HashSet's `add` method
[source, javascript]
Expand All @@ -181,7 +181,7 @@ include::{codedir}/data-structures/sets/hash-set.js[tag=add, indent=0]
}
----

`HashMap` stores key/value pairs, but for this, we only need the key, and we ignore the value.
`HashMap` stores key/value pairs, but we only need the keys for Set, so we ignore the value.

===== Finding values in a HashSet

Expand All @@ -198,7 +198,7 @@ true, and if it’s empty, it will be false.

===== Deleting values from a HashSet

For deleting a value from a hashSet we use the HashMap’s delete method:
For deleting a value from a hashSet, we use the HashMap’s delete method:

.HashSet's `delete` method
[source, javascript]
Expand All @@ -210,7 +210,7 @@ This method has an average runtime of *O(1)*.

==== HashSet vs HashMap Time Complexity

We can say that `HashMap` in on average more performant O(1) vs. O(log n). However, if a
We can say that `HashMap` in on average, more performant O(1) vs. O(log n). However, if a
rehash happens, it will take *O(n)* instead of *O(1)*. A `TreeSet` is always *O(log n)*.

(((Tables, Non-Linear DS, HashSet/TreeSet complexities)))
Expand All @@ -236,3 +236,80 @@ difference besides runtime is that:
.TreeSet vs HashSet
* HashSet keeps data in insertion order
* TreeSet keeps data sorted in ascending order.


==== Practice Questions
(((Interview Questions, Set)))

// tag::set-q-most-common-word[]
===== Most common word

*ST-1*) _Given a text and a list of banned words.
Find the most common word that is not on the banned list.
You might need to sanitize the text and strip out punctuation `?!,'.`_
// end::set-q-most-common-word[]

// _Seen in interviews at: Amazon._

Examples:

[source, javascript]
----
mostCommonWord(
`How much wood, would a Woodchuck chuck,
if a woodchuck could chuck?`,
['a'],
); // woodchuck or chuck (both show up twice)
mostCommonWord(
`It's a blue ball and its shade... Very BLUE!`,
['and']); // blue (it show up twice, "it" and "its" once)
----

Starter code:

[source, javascript]
----
include::../../interview-questions/most-common-word.js[tags=description;placeholder]
----


_Solution: <<set-q-most-common-word>>_











// tag::set-q-longest-substring-without-repeating-characters[]
===== Longest Without Repeating

*ST-2*) _Find the length of the longest substring without repeating characters._

// end::set-q-longest-substring-without-repeating-characters[]

// _Seen in interviews at: Amazon, Facebook, Bloomberg._

Examples:

[source, javascript]
----
lenLongestSubstring('aaaaa'); // 1 ('a')
lenLongestSubstring('abccdefg'); // 5 ('cdefg')
lenLongestSubstring('abc'); // 3 ('abc')
----

Starter code:

[source, javascript]
----
include::../../interview-questions/longest-substring-without-repeating-characters.js[tags=description;placeholder]
----


_Solution: <<set-q-longest-substring-without-repeating-characters>>_
2 changes: 1 addition & 1 deletion book/interview-questions/binary-tree-right-side-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const { Queue } = require('../../src/index');
/**
* Find the rightmost nodes by level.
*
* @example
* @example rightSideView(bt([1,2,3,4])); // [1, 3, 4]
* 1 <- 1
* / \
* 2 3 <- 3
Expand Down
1 change: 1 addition & 0 deletions book/interview-questions/buy-sell-stock.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* maxProfit([1, 2, 3]); // => 2
* maxProfit([3, 2, 1]); // => 0
* @param {number[]} prices - Array with daily stock prices
* @returns {number} - Max profit
*/
function maxProfit(prices) {
// end::description[]
Expand Down
1 change: 1 addition & 0 deletions book/interview-questions/daily-temperatures.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* dailyTemperatures([73, 69, 72, 76, 73]); // [3, 1, 1, 0, 0]
*
* @param {number[]} t - Daily temperatures
* @returns {number[]} - Array with count of days to warmer temp.
*/
function dailyTemperatures(t) {
// end::description[]
Expand Down
5 changes: 3 additions & 2 deletions book/interview-questions/linkedlist-same-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
* hasSameData(['he', 'll', 'o'], ['hel', 'lo']); // true
* hasSameData(['hel', 'lo'], ['hi']); // false
*
* @param {ListNode} l1 - The root node of list 1
* @param {ListNode} l2 - The root node of list 2
* @param {ListNode} l1 - The root node of list 1.
* @param {ListNode} l2 - The root node of list 2.
* @returns {boolean} - true if has same data, false otherwise.
*/
function hasSameData(l1, l2) {
// end::description[]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
// https://leetcode.com/problems/longest-substring-without-repeating-characters/submissions/

function lengthOfLongestSubstring(s: string): number {
// tag::description[]
/**
* Find the length of the longest substring without duplicates.
* @example lenLongestSubstring('abccxyz'); // => 4 (cxyz)
* @param {string} s - The string.
* @returns {number} - The length of the longest unique substring.
*/
function lenLongestSubstring(s) {
// end::description[]
// tag::placeholder[]
// write your code here...
// end::placeholder[]
// tag::solution[]
let max = 0;
const set = new Set();

Expand All @@ -11,4 +21,9 @@ function lengthOfLongestSubstring(s: string): number {
}

return max;
};
// end::solution[]
// tag::description[]
}
// end::description[]

module.exports = { lenLongestSubstring };
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
describe('', () => {
it('', () => {
const { lenLongestSubstring } = require('./longest-substring-without-repeating-characters');
// const { } = require('../../src/index');

[lenLongestSubstring].forEach((fn) => {
describe(`Set: ${fn.name}`, () => {
it('should work with null/empty', () => {
const actual = '';
const expected = 0;
expect(fn(actual)).toEqual(expected);
});

it('should work with small case', () => {
const actual = 'abc';
const expected = 3;
expect(fn(actual)).toEqual(expected);
});

it('should work with other case', () => {
const actual = 'abccdefg';
const expected = 5;
expect(fn(actual)).toEqual(expected);
});
});
});
1 change: 1 addition & 0 deletions book/interview-questions/max-subarray.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* maxSubArray([-3,4,-1,2,1,-5]); // => 6
*
* @param {number[]} a - Array
* @returns {number} - max sum
*/
function maxSubArray(a) {
// end::description[]
Expand Down
1 change: 1 addition & 0 deletions book/interview-questions/merge-lists.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const ListNode = require('../../src/data-structures/linked-lists/node');
*
* @param {ListNode} l1 - The root node of list 1
* @param {ListNode} l2 - The root node of list 2
* @returns {ListNode} - The root of the merged list.
*/
function mergeTwoLists(l1, l2) {
// end::description[]
Expand Down
Loading