Skip to content

Commit

Permalink
Sri Hari: Batch-3/Neetcode-150/Added hints (#3746)
Browse files Browse the repository at this point in the history
* Batch-3/Neetcode-150/Added hints

* Batch-3/Neetcode-150/Added hints
  • Loading branch information
Srihari2222 authored Nov 23, 2024
1 parent ac2ef79 commit df08b81
Show file tree
Hide file tree
Showing 13 changed files with 323 additions and 12 deletions.
12 changes: 6 additions & 6 deletions articles/design-twitter-feed.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,10 @@ class Twitter {

### Time & Space Complexity

* Time complexity: $O(n \log n)$ for $getNewsFeed()$ and $O(1)$ for remaining methods.
* Space complexity: $O(1)$
* Time complexity: $O(n * m + t\log t)$ for each $getNewsFeed()$ call and $O(1)$ for remaining methods.
* Space complexity: $O(N * m + N * M)$

> Where $n$ is the number of tweets associated with the $useId$ and its $followeeIds$.
> Where $n$ is the total number of $followeeIds$ associated with the $userId$, $m$ is the maximum number of tweets by any user, $t$ is the total number of tweets associated with the $userId$ and its $followeeIds$, $N$ is the total number of $userIds$ and $M$ is the maximum number of followees for any user.
---

Expand Down Expand Up @@ -778,7 +778,7 @@ class Twitter {

### Time & Space Complexity

* Time complexity: $O(n+\log n)$ for $getNewsFeed()$ and $O(1)$ for remaining methods.
* Space complexity: $O(1)$
* Time complexity: $O(n)$ for each $getNewsFeed()$ call and $O(1)$ for remaining methods.
* Space complexity: $O(N * m + N * M + n)$

> Where $n$ is the number of tweets associated with the $useId$ and its $followeeIds$.
> Where $n$ is the total number of $followeeIds$ associated with the $userId$, $m$ is the maximum number of tweets by any user, $N$ is the total number of $userIds$ and $M$ is the maximum number of followees for any user.
2 changes: 1 addition & 1 deletion articles/kth-largest-element-in-an-array.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class Solution {

---

## 2. Heap
## 2. Min-Heap

::tabs-start

Expand Down
2 changes: 1 addition & 1 deletion articles/kth-largest-integer-in-a-stream.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class KthLargest(k: Int, nums: IntArray) {

---

## 2. Heap
## 2. Min-Heap

::tabs-start

Expand Down
8 changes: 4 additions & 4 deletions articles/task-scheduling.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ class Solution {
---

## 2. Heap
## 2. Max-Heap

::tabs-start

Expand Down Expand Up @@ -609,7 +609,7 @@ class Solution {
### Time & Space Complexity

* Time complexity: $O(m)$
* Space complexity: $O(m)$
* Space complexity: $O(1)$ since we have at most $26$ different characters.

> Where $m$ is the number of tasks.

Expand Down Expand Up @@ -780,7 +780,7 @@ class Solution {
### Time & Space Complexity

* Time complexity: $O(m)$
* Space complexity: $O(1)$
* Space complexity: $O(1)$ since we have at most $26$ different characters.

> Where $m$ is the number of tasks.

Expand Down Expand Up @@ -956,6 +956,6 @@ class Solution {
### Time & Space Complexity

* Time complexity: $O(m)$
* Space complexity: $O(1)$
* Space complexity: $O(1)$ since we have at most $26$ different characters.

> Where $m$ is the number of tasks.
31 changes: 31 additions & 0 deletions hints/combination-target-sum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<br>
<details class="hint-accordion">
<summary>Recommended Time & Space Complexity</summary>
<p>
You should aim for a solution with <code>O(2^(t/m))</code> time and <code>O(t/m)</code> space, where <code>t</code> is the given <code>target</code> and <code>m</code> is the minimum value in the given array.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 1</summary>
<p>
Can you think of this problem in terms of a decision tree, where at each step, we have <code>n</code> decisions, where <code>n</code> is the size of the array? In this decision tree, we can observe that different combinations of paths are formed. Can you think of a base condition to stop extending a path? Maybe you should consider the target value.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 2</summary>
<p>
We can use backtracking to recursively traverse these paths and make decisions to choose an element at each step. We maintain a variable <code>sum</code>, which represents the sum of all the elements chosen in the current path. We stop this recursive path if <code>sum == target</code>, and add a copy of the chosen elements to the result. How do you implement it?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 3</summary>
<p>
We recursively select elements, increasing the <code>sum</code> and appending the element to the temporary list, which tracks the chosen elements in the current path. At each step, we have the option to consider all elements in the array, but we only proceed with elements that, when added to <code>sum</code>, do not exceed the <code>target</code>. We iterate through the entire array at each step, choosing elements accordingly.
</p>
</details>
39 changes: 39 additions & 0 deletions hints/find-median-in-a-data-stream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<br>
<details class="hint-accordion">
<summary>Recommended Time & Space Complexity</summary>
<p>
You should aim for a solution with <code>O(logn)</code> time for <code>addNum()</code>, <code>O(1)</code> time for <code>findMedian()</code>, and <code>O(n)</code> space, where <code>n</code> is the current number of elements.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 1</summary>
<p>
A naive solution would be to store the data stream in an array and sort it each time to find the median, resulting in <code>O(nlogn)</code> time for each <code>findMedian()</code> call. Can you think of a better way? Perhaps using a data structure that allows efficient insertion and retrieval of the median can make the solution more efficient.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 2</summary>
<p>
If we divide the array into two parts, we can find the median in <code>O(1)</code> if the left half can efficiently return the maximum and the right half can efficiently return the minimum. These values determine the median. However, the process changes slightly if the total number of elements is odd — in that case, the median is the element from the half with the larger size. Can you think of a data structure which is suitable to implement this?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 3</summary>
<p>
We can use a Heap (Max-Heap for the left half and Min-Heap for the right half). Instead of dividing the array, we store the elements in these heaps as they arrive in the data stream. But how can you maintain equal halves of elements in these two heaps? How do you implement this?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 4</summary>
<p>
We initialize a Max-Heap and a Min-Heap. When adding an element, if the element is greater than the minimum element of the Min-Heap, we push it into the Min-Heap; otherwise, we push it into the Max-Heap. If the size difference between the two heaps becomes greater than one, we rebalance them by popping an element from the larger heap and pushing it into the smaller heap. This process ensures that the elements are evenly distributed between the two heaps, allowing us to retrieve the middle element or elements in <code>O(1)</code> time.
</p>
</details>
31 changes: 31 additions & 0 deletions hints/k-closest-points-to-origin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<br>
<details class="hint-accordion">
<summary>Recommended Time & Space Complexity</summary>
<p>
You should aim for a solution as good or better than <code>O(nlogk)</code> time and <code>O(k)</code> space, where <code>n</code> is the size of the input array, and <code>k</code> is the number of points to be returned.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 1</summary>
<p>
A naive solution would be to sort the array in ascending order based on the distances of the points from the origin <code>(0, 0)</code> and return the first <code>k</code> points. This would take <code>O(nlogn)</code> time. Can you think of a better way? Perhaps you could use a data structure that maintains only <code>k</code> points and allows efficient insertion and removal.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 2</summary>
<p>
We can use a Max-Heap that keeps the maximum element at its top and allows retrieval in <code>O(1)</code> time. This data structure is ideal because we need to return the <code>k</code> closest points to the origin. By maintaining only <code>k</code> points in the heap, we can efficiently remove the farthest point when the size exceeds <code>k</code>. How would you implement this?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 3</summary>
<p>
We initialize a Max-Heap that orders points based on their distances from the origin. Starting with an empty heap, we iterate through the array of points, inserting each point into the heap. If the size of the heap exceeds <code>k</code>, we remove the farthest point (the maximum element in the heap). After completing the iteration, the heap will contain the <code>k</code> closest points to the origin. Finally, we convert the heap into an array and return it.
</p>
</details>
39 changes: 39 additions & 0 deletions hints/kth-largest-element-in-an-array.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<br>
<details class="hint-accordion">
<summary>Recommended Time & Space Complexity</summary>
<p>
You should aim for a solution as good or better than <code>O(nlogk)</code> time and <code>O(k)</code> space, where <code>n</code> is the size of the input array, and <code>k</code> represents the rank of the largest number to be returned (i.e., the <code>k-th</code> largest element).
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 1</summary>
<p>
A naive solution would be to sort the array in descending order and return the <code>k-th</code> largest element. This would be an <code>O(nlogn)</code> solution. Can you think of a better way? Maybe you should think of a data structure which can maintain only the top <code>k</code> largest elements.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 2</summary>
<p>
We can use a Min-Heap, which stores elements and keeps the smallest element at its top. When we add an element to the Min-Heap, it takes <code>O(logk)</code> time since we are storing <code>k</code> elements in it. Retrieving the top element (the smallest in the heap) takes <code>O(1)</code> time. How can this be useful for finding the <code>k-th</code> largest element?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 3</summary>
<p>
The <code>k-th</code> largest element is the smallest element among the top <code>k</code> largest elements. This means we only need to maintain <code>k</code> elements in our Min-Heap to efficiently determine the <code>k-th</code> largest element. Whenever the size of the Min-Heap exceeds <code>k</code>, we remove the smallest element by popping from the heap. How do you implement this?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 4</summary>
<p>
We initialize an empty Min-Heap. We iterate through the array and add elements to the heap. When the size of the heap exceeds <code>k</code>, we pop from the heap and continue. After the iteration, the top element of the heap is the <code>k-th</code> largest element.
</p>
</details>
39 changes: 39 additions & 0 deletions hints/kth-largest-integer-in-a-stream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<br>
<details class="hint-accordion">
<summary>Recommended Time & Space Complexity</summary>
<p>
You should aim for a solution with <code>O(mlogk)</code> time and <code>O(k)</code> space, where <code>m</code> is the number of times <code>add()</code> is called, and <code>k</code> represents the rank of the largest number to be tracked (i.e., the <code>k-th</code> largest element).
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 1</summary>
<p>
A brute force solution would involve sorting the array in every time a number is added using <code>add()</code>, and then returning the <code>k-th</code> largest element. This would take <code>O(m * nlogn)</code> time, where <code>m</code> is the number of calls to <code>add()</code> and <code>n</code> is the total number of elements added. However, do we really need to track all the elements added, given that we only need the <code>k-th</code> largest element? Maybe you should think of a data structure which can maintain only the top <code>k</code> largest elements.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 2</summary>
<p>
We can use a Min-Heap, which stores elements and keeps the smallest element at its top. When we add an element to the Min-Heap, it takes <code>O(logk)</code> time since we are storing <code>k</code> elements in it. Retrieving the top element (the smallest in the heap) takes <code>O(1)</code> time. How can this be useful for finding the <code>k-th</code> largest element?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 3</summary>
<p>
The <code>k-th</code> largest element is the smallest element among the top <code>k</code> largest elements. This means we only need to maintain <code>k</code> elements in our Min-Heap to efficiently determine the <code>k-th</code> largest element. Whenever the size of the Min-Heap exceeds <code>k</code>, we remove the smallest element by popping from the heap. How do you implement this?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 4</summary>
<p>
We initialize a Min-Heap with the elements of the input array. When the <code>add()</code> function is called, we insert the new element into the heap. If the heap size exceeds <code>k</code>, we remove the smallest element (the root of the heap). Finally, the top element of the heap represents the <code>k-th</code> largest element and is returned.
</p>
</details>
23 changes: 23 additions & 0 deletions hints/last-stone-weight.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<br>
<details class="hint-accordion">
<summary>Recommended Time & Space Complexity</summary>
<p>
You should aim for a solution as good or better than <code>O(nlogn)</code> time and <code>O(n)</code> space, where <code>n</code> is the size of the input array.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 1</summary>
<p>
A naive solution would involve simulating the process by sorting the array at each step and processing the top <code>2</code> heaviest stones, resulting in an <code>O(n * nlogn)</code> time complexity. Can you think of a better way? Consider using a data structure that efficiently supports insertion and removal of elements and maintain the sorted order.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 2</summary>
<p>
We can use a Max-Heap, which allows us to retrieve the maximum element in <code>O(1)</code> time. We initially insert all the weights into the Max-Heap, which takes <code>O(logn)</code> time per insertion. We then simulate the process until only one or no element remains in the Max-Heap. At each step, we pop two elements from the Max-Heap which takes <code>O(logn)</code> time. If they are equal, we do not insert anything back into the heap and continue. Otherwise, we insert the difference of the two elements back into the heap.
</p>
</details>
31 changes: 31 additions & 0 deletions hints/permutations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<br>
<details class="hint-accordion">
<summary>Recommended Time & Space Complexity</summary>
<p>
You should aim for a solution with <code>O(n * n!)</code> time and <code>O(n)</code> space, where <code>n</code> is the size of the input array.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 1</summary>
<p>
A permutation is the same as the array but with the numbers arranged in a different order. The given array itself is also considered a permutation. This means we should make a decision at each step to take any element from the array that has not been chosen previously. By doing this recursively, we can generate all permutations. How do you implement it?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 2</summary>
<p>
We can use backtracking to explore all possible permutation paths. We initialize a temporary list to append the chosen elements and a boolean array of size <code>n</code> (the same size as the input array) to track which elements have been picked so far (<code>true</code> means the element is chosen; otherwise, <code>false</code>). At each step of recursion, we iterate through the entire array, picking elements that have not been chosen previously, and proceed further along that path. Can you think of the base condition to terminate the current recursive path?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 3</summary>
<p>
We observe that every permutation has the same size as the input array. Therefore, we can append a copy of the list of chosen elements in the current path to the result list if the size of the list equals the size of the input array terminating the current recursive path.
</p>
</details>
39 changes: 39 additions & 0 deletions hints/subsets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<br>
<details class="hint-accordion">
<summary>Recommended Time & Space Complexity</summary>
<p>
You should aim for a solution with <code>O(n * (2^n))</code> time and <code>O(n)</code> space, where <code>n</code> is the size of the input array.
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 1</summary>
<p>
It is straightforward that if the array is empty we return an empty array. When we have an array <code>[1]</code> which is of size <code>1</code>, we have two subsets, <code>[[], [1]]</code> as the output. Can you think why the output is so?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 2</summary>
<p>
We can see that one subset includes a number, and another does not. From this, we can conclude that we need to find the subsets that include a number and those that do not. This results in <code>2^n</code> subsets for an array of size <code>n</code> because there are many combinations for including and excluding the array of numbers. Since the elements are unique, duplicate subsets will not be formed if we ensure that we don't pick the element more than once in the current subset. Which algorithm is helpful to generate all subsets, and how would you implement it?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 3</summary>
<p>
We can use backtracking to generate all possible subsets. We iterate through the given array with an index <code>i</code> and an initially empty temporary list representing the current subset. We recursively process each index, adding the corresponding element to the current subset and continuing, which results in a subset that includes that element. Alternatively, we skip the element by not adding it to the subset and proceed to the next index, forming a subset without including that element. What can be the base condition to end this recursion?
</p>
</details>

<br>
<details class="hint-accordion">
<summary>Hint 4</summary>
<p>
When the index <code>i</code> reaches the end of the array, we append a copy of the subset formed in that particular recursive path to the result list and return. All subsets of the given array are generated from these different recursive paths, which represent various combinations of "include" and "not include" steps for the elements of the array. As we are only iterating from left to right in the array, we don't pick an element more than once.
</p>
</details>
Loading

0 comments on commit df08b81

Please sign in to comment.