Skip to content

Commit

Permalink
Revert "(book/exercises): interview q&a for linked lists and stacks (#69
Browse files Browse the repository at this point in the history
)" (#70)

This reverts commit ca4bf45.
  • Loading branch information
amejiarosario committed Aug 26, 2020
1 parent ca4bf45 commit 06bd3f6
Show file tree
Hide file tree
Showing 14 changed files with 56 additions and 658 deletions.
206 changes: 3 additions & 203 deletions book/D-interview-questions-solutions.asc
@@ -1,4 +1,3 @@
<<<
[appendix]
[[d-interview-questions-solutions]]
== Interview Questions Solutions
Expand Down Expand Up @@ -30,7 +29,7 @@ include::interview-questions/max-subarray.js[tag=maxSubArrayBrute1]

This code is simple to understand; however, not very efficient. The runtime is `O(n^3)`.

Notice we're adding up the numbers from `i` to `j` on each cycle. But, we can optimize this. We can keep a local variable and add the new number to it. That way, we don't have to revisit previous numbers.
If you noticed we adding up the numbers from `i` to `j` on each cycle. But, we can optimize this. We can keep a local variable and add the new number to it. That way, we don't have to revisit previous numbers.

[source, javascript]
----
Expand All @@ -47,7 +46,7 @@ include::interview-questions/max-subarray.js[tag=description]
include::interview-questions/max-subarray.js[tag=solution]
----

The runtime is `O(n)` and space complexity of `O(1)`.
The runtime is `O(n)` and a space complexity of `O(1)`.



Expand Down Expand Up @@ -94,203 +93,4 @@ include::interview-questions/buy-sell-stock.js[tag=description]
include::interview-questions/buy-sell-stock.js[tag=solution]
----

The runtime is `O(n)` and space complexity of `O(1)`.



:leveloffset: +1

=== Solutions for Linked List Questions
(((Interview Questions Solutions, Linked Lists)))

:leveloffset: -1




[#linkedlist-q-merge-lists]
include::content/part02/linked-list.asc[tag=linkedlist-q-merge-lists]

We need to visit each node in both lists and merge them in ascending order. Note: We don't need to copy the values nor create new nodes.

Another case to take into consideration is that lists might have different lengths. So, if one list runs out, we have to keep taking elements from the remaining list.

*Algorithm*:

- Have a pointer for each list
- While there's a pointer that is not null, visite them
- Compare each list node's value and take the smaller one.
- Advance the pointer of the taken node to the next one.

*Implementation*:

[source, javascript]
----
include::interview-questions/merge-lists.js[tag=description]
include::interview-questions/merge-lists.js[tag=solution]
----

Notice that we used a "dummy" node or "sentinel node" to have some starting point for the final list.

*Complexity Analysis*:

- Time: `O(m+n)`. Visiting each node from the list 1 and list 2 has a time complexity `O(m + n)`. `m` and `n` represent each list's length.
- Space: `O(1)`. We reuse the same nodes and only change their `next` pointers. We only create one additional node, "the sentinel node."


[#linkedlist-q-linkedlist-same-data]
include::content/part02/linked-list.asc[tag=linkedlist-q-linkedlist-same-data]

We are given two linked lists that contain string data. We want to know if the concatenated strings from each list are the same.

The tricky part is that the same data can be distributed differently on the linked lists:

----
L1: he -> ll -> o
L2: h -> e -> llo
----

One naive approach could be to go through each list's node and concatenate the strings. Then, we can check if they are equal.

[source, javascript]
----
include::interview-questions/linkedlist-same-data.js[tag=hasSameDataBrute1]
----

Notice that the problem mentions that lists could be huge (millions of nodes). If the first character on each list is different, we are unnecessarily computing millions of nodes, when a straightforward check will do the job.

A better way to solve this problem is iterating over each character on both lists, and when we found mistmatch, we return `false` immediately. If they are the same, we still have to visit all of them.

*Algorithm*:

- Set a pointer to iterate over each node in the lists.
- For each node, have an index (starting at zero) and compare if both lists have the same data.
- When the index reaches the last character on the current node, we move to the next node.
- If we found that a character from one list doesn't match the other, we return `false`.

*Implementation*:

[source, javascript]
----
include::interview-questions/linkedlist-same-data.js[tag=description]
include::interview-questions/linkedlist-same-data.js[tag=solution]
----

The function `findNextPointerIndex` is a helper to navigate each character on a linked list.
Notice, that we increase the index (`i + 1`) on each iteration.
If the index overflows, it moves to the next node and reset the index to zero.



*Complexity Analysis*:

- Time: `O(n)`. We go over all the characters on each list
- Space: `O(1)`. Only using pointers and no auxiliary data structures.



:leveloffset: +1

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

:leveloffset: -1

[#stack-q-valid-parentheses]
include::content/part02/stack.asc[tag=stack-q-valid-parentheses]

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

This is a parsing problem, and usually, stacks are good candidates for them.

*Algorithm*:

- Create a mapping for each opening bracket, to its closing counterpart.
- Iterate through the string
- When we found an opening bracket, insert the corresponding closing bracket into the stack.
- When we found a closing bracket, pop from the stack and make sure it corresponds to the current character.
- Check the stack is empty. If there's a leftover, it means that something didn't close properly.

*Implementation*:

[source, javascript]
----
include::interview-questions/valid-parentheses.js[tag=description]
include::interview-questions/valid-parentheses.js[tag=solution]
----

*Complexity Analysis*:

- Time: `O(n)`. We iterate over each character of the string.
- Space: `O(n)`. We use an auxiliary stack.



[#stack-q-daily-temperatures]
include::content/part02/stack.asc[tag=stack-q-daily-temperatures]

The first solution that might come to mind it's using two for loops. For each element, we have visit each temperature ahead to find a bigger one.

[source, javascript]
----
include::interview-questions/daily-temperatures.js[tag=dailyTemperaturesBrute1]
----

This solution is an `O(n^2)`. Can we do better? We can!

Here's an idea: start backward, so we know when there's a warmer temperature beforehand. The last element is always 0 (because there are no more temperatures ahead of it). We can place each element's index that we visit on a stack. If the current weather is bigger than the stack top, we remove it until a bigger one remains or the stack is empty. If the stack has a value, we calculate the number of days ahead. Otherwise, it is 0.

*Algorithm*:

- Traverse the daily temperatures backward
- Push each temperature to a stack.
- While the current temperature is larger than the one at the top of the stack, pop it.
- If the stack is empty, then there's no warmer weather ahead, so it's 0.
- If the stack has an element, calculate the index delta.

*Implementation*:

[source, javascript]
----
include::interview-questions/daily-temperatures.js[tag=description]
include::interview-questions/daily-temperatures.js[tag=solution]
----

The stack contains the indexes rather than the temperatures themselves.

*Complexity Analysis*:

- Time: `O(n)`. We visit each element on the array once.
- Space: `O(1)`. The worst-case scenario is ascending order without duplicates. The stack will hold at most 70 items (100 - 30). If we didn't have the range restriction, then space complexity would be `O(n)`.



// [#linkedlist-q-FILENAME]
// include::content/part02/linked-list.asc[tag=linkedlist-q-FILENAME]

// RESTATE REQUIREMENTS AND DESCRIPTIONS

// *Algorithm*:

// - STEP 1
// - STEP 2
// - STEP 2.1
// - STEP 2.2

// *Implementation*:

// [source, javascript]
// ----
// include::interview-questions/FILENAME.js[tag=description]
// include::interview-questions/FILENAME.js[tag=solution]
// ----

// IMPLEMENTATION NOTES

// *Complexity Analysis*:

// - Time: `O(?)`. WHY?
// - Space: `O(?)`. WHY?

The runtime is `O(n)` and a space complexity of `O(1)`.
2 changes: 1 addition & 1 deletion book/config
34 changes: 17 additions & 17 deletions book/content/part02/array.asc
Expand Up @@ -7,30 +7,30 @@ endif::[]
=== Array [[array-chap]]
(((Array)))
(((Data Structures, Linear, Array)))
Arrays are one of the most used data structures. You probably have used it a lot, but are you aware of the runtimes of `splice`, `shift`, `indexOf`, and other operations? In this chapter, we are going deeper into the most common operations and their runtimes.
Arrays are one of the most used data structures. You probably have used it a lot but are you aware of the runtimes of `splice`, `shift`, `indexOf` and other operations? In this chapter, we are going deeper into the most common operations and their runtimes.

==== Array Basics

An array is a collection of things (strings, characters, numbers, objects, etc.). They can be many or zero.

TIP: Strings are a collection of Unicode characters, and most of the array concepts apply to them.
TIP: Strings are a collection of Unicode characters and most of the array concepts apply to them.

.Fixed vs. Dynamic Size Arrays
****
Some programming languages have fixed-size arrays like Java and {cpp}.
Fixed-size arrays might be a hassle when your collection gets full, and you have to create a new one with a bigger size. Those programming languages also have built-in dynamic arrays: we have `vector` in {cpp} and `ArrayList` in Java. Dynamic programming languages like JavaScript, Ruby, and Python use dynamic arrays by default.
Some programming languages have fixed size arrays like Java and {cpp}.
Fixed size arrays might be a hassle when your collection gets full, and you have to create a new one with a bigger size. For that, those programming languages also have built-in dynamic arrays: we have `vector` in {cpp} and `ArrayList` in Java. Dynamic programming languages like JavaScript, Ruby, and Python use dynamic arrays by default.
****

Arrays look like this:

.Array representation: each value is accessed through an index.
image::image16.png[image,width=388,height=110]

Arrays are a sequential collection of elements that can be accessed randomly using an index. Let’s take a look at the different operations that we can do with arrays.
Arrays are a sequential collection of elements that can be accessed randomly using an index. Let’s take a look into the different operations that we can do with arrays.

==== Insertion

Arrays are built-in in most languages. Inserting an element is simple; you can either add them at creation time or after initialization. Below you can find an example for both cases:
Arrays are built-in into most languages. Inserting an element is simple; you can either add them at creation time or after initialization. Below you can find an example for both cases:

.Inserting elements into an array
[source, javascript]
Expand All @@ -45,7 +45,7 @@ array2[100] = 2;
array2 // [empty × 3, 1, empty × 96, 2]
----

Using the index, you can replace whatever value you want. Also, you don't have to add items next to each other. The size of the array will dynamically expand to accommodate the data. You can reference values at whatever index you like: index 3 or even 100! In `array2`, we inserted 2 numbers, but the length is 101, and there are 99 empty spaces.
Using the index, you can replace whatever value you want. Also, you don't have to add items next to each other. The size of the array will dynamically expand to accommodate the data. You can reference values at whatever index you like: index 3 or even 100! In `array2`, we inserted 2 numbers but the length is 101 and there are 99 empty spaces.

[source, javascript]
----
Expand All @@ -54,7 +54,7 @@ console.log(array2); // [empty × 3, 1, empty × 96, 2]
----


The runtime for inserting elements using an index is always is constant: _O(1)_.
The runtime for inserting elements using index is always is constant: _O(1)_.

===== Inserting at the beginning of the array

Expand All @@ -72,7 +72,7 @@ As you can see, `2` was at index 0, now was pushed to index 1, and everything el

.JavaScript built-in `array.unshift`
****
The `unshift()` method adds one or more elements to the beginning of an array and returns the array's new length.
The `unshift()` method adds one or more elements to the beginning of an array and returns the new length of the array.
Runtime: O(n).
****
Expand All @@ -90,11 +90,11 @@ array.splice(1, 0, 111); // ↪️ [] <1>
----
<1> at position `1`, delete `0` elements and insert `111`.

The Big O for this operation would be *O(n)* since, in the worst case, it would move most of the elements to the right.
The Big O for this operation would be *O(n)* since in worst case it would move most of the elements to the right.

.JavaScript built-in `array.splice`
****
The `splice()` method changes an array's contents by removing existing elements or adding new elements. Splice returns an array containing the deleted items.
The `splice()` method changes the contents of an array by removing existing elements or adding new elements. Splice returns an array containing the deleted elements.
Runtime: O(n).
****
Expand All @@ -116,15 +116,15 @@ Adding to the tail of the array doesn’t change other indexes. E.g., element 2

.JavaScript built-in `array.push`
****
The `push()` method adds one or more elements to the end of an array and returns the array's new length.
The `push()` method adds one or more elements to the end of an array and returns the new length of the array.
Runtime: O(1).
****

[[array-search-by-value]]
==== Searching by value and index

Searching by the index is very easy using the `[]` operator:
Searching by index is very easy using the `[]` operator:

.Search by index
[source, javascript]
Expand Down Expand Up @@ -185,7 +185,7 @@ We would have to loop through the whole array (worst case) or until we find it:

==== Deletion

There are three possible deletion scenarios (similar to insertion): removing at the beginning, middle, or end.
There are three possible scenarios for deletion (similar to insertion): removing at the beginning, middle or end.

===== Deleting element from the beginning

Expand Down Expand Up @@ -224,7 +224,7 @@ array.splice(2, 1); // ↪️[2] <1>
----
<1> delete 1 element at position 2

Deleting from the middle might cause most of the array elements to move up one position to fill in for the eliminated item. Thus, runtime: O(n).
Deleting from the middle might cause most of the elements of the array to move up one position to fill in for the eliminated item. Thus, runtime: O(n).

===== Deleting element from the end

Expand Down Expand Up @@ -282,7 +282,7 @@ To sum up, the time complexity of an array is:
// tag::array-q-max-subarray[]
===== Max Subarray

*AR-1*) _Given an array of integers, find the maximum sum of consecutive elements (subarray)._
Given an array of integers, find the maximum sum of consecutive elements (subarray).
// end::array-q-max-subarray[]

[source, javascript]
Expand All @@ -297,7 +297,7 @@ _Solution: <<array-q-max-subarray>>_
// tag::array-q-buy-sell-stock[]
===== Best Time to Buy and Sell an Stock

*AR-2*) _You are given an array of integers. Each value represents the closing value of the stock on that day. You are only given one chance to buy and then sell. What's the maximum profit you can obtain? (Note: you have to buy first and then sell)_
You are given an array of integers. Each value represents the closing value of the stock on that day. You are only given one chance to buy and then sell. What's the maximun profit you can obtain? (Note: you have to buy first and then sell)
// end::array-q-buy-sell-stock[]

[source, javascript]
Expand Down

0 comments on commit 06bd3f6

Please sign in to comment.