When you're given a question that revolves around keeping track of k elements, a good bet is to think about solving the problem with heaps.

Lets say you're given a problem like the following:

Given an unsorted array of numbers, find the `k`

largest numbers in it.

Example:

```
Input: [3, 1, 5, 13, 2, 12], K = 3
Output: [5, 13, 12]
```

First that comes to mind? Let's sort it and return the largest `k`

elements.

Easy!

Unfortunately this will yield a time complexity of O(nlogn) and guess what? We can do better.

If we iterate through the list one at a time, we can maintain a list of `k`

elements by adding the current element to a min heap. If the length of the min heap becomes greater than `k`

then all we need to do is pop off the smallest element which is conveniently stored at the top of the heap!

Popping off the smallest element of the heap takes O(logk) complexity due to the reshuffling that needs to happen to re-order the remaining elements in the heap.

Given the example above, we can see the algorithm at work here:

- We add to the heap until we've got 3 elements.
- We add 13 to the heap.
`len(heap) > 3`

so we pop off the top element and reshuffle. - 2 is smaller than the top element of the heap so we ignore it.
- We add 12 to the heap.
`len(heap) > 3`

so we pop off the top element and reshuffle.

```
def find_k_largest(arr: List[int], k: int) -> List[int]:
min_heap = []
for num in arr[0:k]:
heappush(min_heap, num)
for num in arr[k:]:
if num > min_heap[0]:
heappop(min_heap)
heappush(min_heap, num)
return list(min_heap)
```

Here's some extra questions to get you familiar with the pattern:

https://leetcode.com/problems/third-maximum-number/ (Easy)

https://leetcode.com/problems/top-k-frequent-elements/ (Medium)

https://leetcode.com/problems/kth-largest-element-in-an-array/ (Medium)

]]>Lets work through this problem together:

Implement a class that can calculate the median of a number stream. The class should have two methods: `add_num`

(adds a number to the class) and `find_median`

(finds the median of the stored numbers).

Note: If the total numbers inserted into the class is even, you should take the average of the two middle numbers

Example:

```
1. add_num(5)
2. add_num(1)
3. find_median() -> output: (5+1)/2 = 3
4. add_num(8)
5. find_median() -> output: 5
6. add_num(6)
7. find_median() -> output: (6+5)/2 = 5.5
```

Can you figure out a brute force solution?

We can solve this inefficiently by maintaining an ordered list of values and then returning the median whenever we need to. Unfortunately, inserting a number into a sorted list will take O(N) and we can do better!

Notice that we don't need a fully ordered list; we only need to identify the middle element(s).

We notice that for every middle element, half of the list will be smaller than or equal to the middle element and half will be greater than or equal to the middle element.

So why don't we have two lists? One for elements that are smaller (lets call it `small_elements`

) and one for elements that are larger (lets call it `large_elements`

). If we have two lists, the median of the two lists will be either the largest in `small_elements`

or the smallest in the `large_element`

or if the total elements are even, then it would be the average of the two numbers.

So how can we maintain the smallest/largest in a list? Using a heap of-course!

`small_elements`

will be stored as a**max heap.**`large_elements`

will be stored as a**min heap.**

But why does this make our solution better? This solution is better because inserting into a heap is a O(logN) operation, rather than an O(N) operation that we were doing before.

Lets look at a diagram and see how this works (given the example above):

- We can insert into the max heap if the top element (i.e. the greatest element) is smaller than the element we're inserting. After each insertion, we need to remember to balance the heaps so that we have an even number of elements in each. If it's an odd number, lets leave more elements in the max-heap rather than the min-heap. (You can decide to go the other way but the implementation will slightly differ)
- As
`1`

is smaller than`5`

we can add it to the max heap. Now that the heaps are in a state of unbalance; theres two elements in max heap and none in min heap. We have to balance the heaps. So we move the`5`

to the min heap. - We find the median. In this case it's (1+5 ) / 2 = 3 because we have an even number of total elements.
`8`

is larger than the top element of the max heap`1`

, so we add it to the min heap and then balance. Now that the min heap is larger than the max heap, we balance the other way; we move the smallest number from the min heap and insert it into the max heap.- We find median again which in this case is just the top element of the max heap;
`5`

- Insert
`6`

into the min heap as it's greater than`5`

. The heaps are balanced, so there's no need to rebalance. - find median: ( 6+ 5) / 2 = 5.5

```
from heapq import *
class MedianStream:
def __init__(self) -> None:
self.max_heap = []
self.min_heap = []
def find_median(self) -> float:
if len(self.max_heap) == len(self.min_heap):
return -self.max_heap[0] / 2.0 + self.min_heap[0] / 2.0
return -self.max_heap[0] / 1.0
def add_num(self, num: int) -> None:
if not self.max_heap or -self.max_heap[0] >= num:
heappush(self.max_heap, -num)
else:
heappush(self.min_heap, num)
self._rebalance()
def _rebalance(self) -> None:
if len(self.max_heap) > len(self.min_heap) + 1:
heappush(self.min_heap, -heappop(self.max_heap))
elif len(self.max_heap) < len(self.min_heap):
heappush(self.max_heap, -heappop(self.min_heap))
```

You may be confused as to why we're taking the negative here: `-self.max_heap[0]`

or here: `heappush(self.max_heap, -num)`

. It's because the heapq library only supports a min heap. So by taking the negative we're imitating a max heap.

Here's a few more questions to get your head around it:

https://leetcode.com/problems/sliding-window-median/ (Hard)

Thanks!

]]>Hopefully with this pattern, you'll love questions which ask you to reverse the links between a set of nodes of a linked list. It may sounds like a niche pattern but it's one that comes up time and time again.

So how do we do it?

We reverse a linked list one node at a time. We have three variables: `current`

, `previous`

and `temp`

. Juggling these pointers is what allows you to solve this question.

For each step, we reverse the `current`

node by pointing it's `next`

value to the `previous`

pointer. Simple right? Not so much... we need to be careful when we do this because if we lose a reference to a node, that node is lost! Thats where the `temp`

variable comes into play; to ensure that we're keeping track of everything.

It sounds a bit confusing but let me show you via some diagrams:

Note: the `null`

at the beginning isn't the head. I've put it there to make the diagram more understandable.

- We set the
`current`

pointer to the`head`

value, the`temp`

and`previous`

values to`None`

/`null`

(depending on your programming language). - We set the
`temp`

value to`current.next`

. Think about what happens if we don't do this step. - We set the
`current.next`

value to`previous`

; essentially turning the link from → to ←. - We move the
`previous`

pointer to`current`

and then we move the`current`

pointer up one element.`current = current.next`

. If we move the`current`

element first, we will lose our reference to that node!

```
class LinkedListNode:
def __init__(self, value, next_node):
self.value = value
self.next_node = next_node
def reverse_linked_list(head: LinkedListNode):
previous_node, temp_node, current_node = None, None, head
while current_node is not None:
temp_node = current_node.next_node
current_node.next_node = previous_node
previous_node = current_node
current_node = temp_node
return previous_node
```

Work through each step one by one and you'll be able to crack it!

Here's some more questions that you can have a crack at:

https://leetcode.com/problems/palindrome-linked-list/ (Easy)

https://leetcode.com/problems/reverse-linked-list-ii/ (Medium)

If you enjoyed this article, please go support the people over at https://www.educative.io/courses/grokking-the-coding-interview I've based this blog post on their content.

]]>How would you solve the following problem?

**Given n distinct numbers from 0 to n. Find the missing number.**

Key words to pick out here: **distinct**, **0 to n (range)**

Since we know that the numbers range from 0 to n, why don't we try and put them in their correct places? Once we put all the numbers that we have in their correct positions, we can easily determine the numbers that are missing.

Tip: Keep in mind that the range is 0 → n. The solution slightly differs if it's 1 → n. You should notice this in the interview.

```
Input: [4, 0, 3, 1]
Output: 2
```

The trick here is to sort the array in O(N) time whilst maintaining a constant time complexity O(1).

That's where cyclic sort comes in!

Heres the simple idea: We iterate the array one at a time, if the current number is not at the correct index, we swap it with the number at the correct index.

This should give you a better idea of what's going on:

Now, once we've sorted the values, we can then loop through the array and check where `nums[i] != i`

Pretty sweet!

```
def find_missing_number(nums: List[int]) -> int:
i, n = 0, len(nums)
while i < n:
current_num = nums[i]
if current_num < n and current_num != nums[current_num]:
nums[i], nums[current_num] = nums[current_num], nums[i]
else:
i += 1
for idx, num in enumerate(nums):
if idx != num:
return idx
return n # if all the other elements are present, the missing number must be n
```

Tip: This version of the cyclic sort algorithm only works if there are distinct elements. If there aren't distinct elements you need be a bit clever and amend this algorithm a tiny bit. I'm going to let you figure this out!

Here's some other questions:

https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/ (Easy)

https://leetcode.com/problems/find-all-duplicates-in-an-array/ (Medium)

If you enjoyed this article, please go support the people over at https://www.educative.io/courses/grokking-the-coding-interview I've based this blog post on their content.

]]>First step to answering a question like this is to answer the following question: How many ways can two intervals be positioned in relation to each other?

The answer is 6:

This diagram is key to our understanding of intervals! Now let's use this understanding to solve the following problem:

Given a list of intervals, merge all the overlapping intervals.

```
Input: intervals = [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
```

Although the input is sorted in the example given, this may not always be the case. The first step is to sort the input list so that `a.start <= b.start`

. This is will make things a lot simpler as you will see in a second.

One thing to notice, is that we only care about the cases where the intervals overlap. If we look at the previous image, the cases where the intervals overlap are: `2, 3, 4, 5`

. Also, we only care about cases where `a.start <= b.start`

. So that eliminates `5`

. Now we're only left with `2, 3, 4`

!

Ok, we've simplified the problem a bit. Now lets look at how we would merge the cases `2, 3, 4`

:

Can you notice the pattern with the start and end values of each merged interval?

Yep thats right:

- The start of the interval is always
`a.start`

- The end of the interval is the maximum between
`a.end`

and`b.end`

.

Lets look at some code to cement this idea:

```
class Interval:
def __init__(self, start, end):
self.start = start
self.end = end
def merge_intervals(intervals: List[List[int, int]]) -> List[List[int, int]]:
intervals.sort(key=lambda x: x[0])
intervals = [Interval(interval[0], interval[1]) for interval in
intervals] # This is not needed but makes the code more legible.
merged_intervals = []
start = intervals[0].start
end = intervals[0].end
for interval in intervals[1:]:
if interval.start <= end:
end = max(end, interval.end)
else:
merged_intervals.append([start, end])
start = interval.start
end = interval.end
merged_intervals.append([start, end]) # add the last interval
return merged_intervals
```

The key to solving problems like these is to draw out the possible arrangement of intervals in relation to each other and work through the scenarios.

Here's some more questions to put you to the test:

https://leetcode.com/problems/insert-interval/ (Medium)

https://leetcode.com/problems/interval-list-intersections/ (Medium)

https://leetcode.com/problems/my-calendar-i/ (Medium)

If you enjoyed this article, please go support the people over at https://www.educative.io/courses/grokking-the-coding-interview I've based this blog post on their content.

]]>Also known as:

- Hare and Tortoise algorithm.
- Floyd's algorithm.

The pattern is famously used for cycle detection but can be used to solve other sorts of problems too. This is such a good example of a simple but very effective algorithm.

By moving two pointers at different speeds, the algorithm proves that the two pointers are bound to meet. Here's a great StackOverflow post on the proof behind the algorithm: https://cs.stackexchange.com/a/45540

So, let's solve the following question:

Given the head of a singly-linked list, how would you go about determining whether there is a cycle in it or not?

Cast your mind back to the old fable of the tortoise and the hare. Imagine them both on the track (In our case, the linked list). Now, we know that if the track is circular, then the faster hare will be able to eventually catch up and lap the tortoise from behind eventually. This is the same concept behind the tortoise and hare algorithm.

We traverse the linked list with a slow and fast pointer. At each iteration of the traversal, the slower pointer moves one element forward and the fast pointer moves two elements forward. If there's a cycle, they are eventually going to meet.

So what happens if there isn't a cycle in the linked list? Well, in that case the fast pointer will reach the tail of the linked list and hit a null value (indicating the tail) and we will conclude that the linked list doesn't have a cycle.

Pretty nifty eh?

Here's a visual representation to get your head around it:

And here's some python code:

```
def has_cycle(head: LinkedListNode):
slow, fast = head, head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if slow == fast:
return True
return False
```

And that's it! Like I said before: Simple yet effective.

Some extra questions for you to have a go at:

https://leetcode.com/problems/middle-of-the-linked-list/ (Easy)

https://leetcode.com/problems/happy-number/ (Easy)

https://leetcode.com/problems/linked-list-cycle-ii/ (Medium)

If you enjoyed this article, please go support the people over at https://www.educative.io/courses/grokking-the-coding-interview/. I've based this blog post on their content.

]]>**Given an array of sorted numbers and a target sum, find a pair in the array whose sum is equal to the given target.**

A brute force solution to this question would be to:

- Look at each element one by one.
- Iterate through the remaining elements using a second pointer to find the pair with the target sum.

The time complexity would be O(N^2)! We can do better.

One thing to notice is that the array is **sorted**. So what we can do is add a pointer at the beginning, and one at the end. At every step we will converge to the target by:

- If sum(left, right) > target → move the right pointer one element to the left (
`right -= 1`

). Because the array is sorted, this has the effect of**reducing**the current sum. - If sum(left, right) < target → move the left pointer one element to the right (left
`+= 1`

). Because the array is sorted, this has the effect of**increasing**the current sum.

Heres some python code:

```
def pair_with_target(arr: List[int], target:int):
left_idx = 0
right_idx = len(arr) - 1
while left_idx < right_idx:
current_sum = arr[left_idx] + arr[right_idx]
if current_sum == target:
return [arr[left_idx], arr[right_idx]]
if current_sum > target:
# Reduce the current sum by moving right_idx to the left
right_idx -= 1
else:
# Increase the current sum by moving left_idx to the right
left_idx += 1
return [-1, -1] # If we don't find a pair
```

Here's some related questions to put you to the test:

https://leetcode.com/problems/squares-of-a-sorted-array/ (Easy) https://leetcode.com/problems/3sum/ (Medium) https://leetcode.com/problems/sort-colors/ (Medium)

Also there's a list of these sorts of problems here:

https://leetcode.com/tag/two-pointers/

Note: These blog posts are based on https://www.educative.io/courses/grokking-the-coding-interview/. Please go ahead and purchase the course if you find these useful!

]]>Alright, so lets get started.

There are quite a few questions that are asked which require you to work out something among a **continuous subarray ** of a given size.

At the beginning of my grind, I struggled to come up with a brute force solution to such questions. So in this article, I'm going to describe both the brute force solution and also the optimal solution to the following question:

Given an array, find the average of all contiguous subarrays of size ‘K’ in it.

```
Array: [1, 3, 2, 6, -1, 4, 1, 8, 2], K=5
```

One way of solving this would be to:

- Find the average of the first five numbers [0:6]
- Find the average of the next five numbers [1:7]
- Find the average of the next five number [2:8]
- and so on..

The code for this would look something like:

```
def find_subarray_averages_of_size_k(k: int, arr: List[float]) -> List[float]:
result = []
for i in range(len(arr) - k + 1):
subarr_sum = 0
for j in range(i, i + k):
subarr_sum += arr[j]
result.append(subarr_sum / k)
return result
```

So what's the time complexity for something like this? I'd suggest you try working this out yourself.

Time complexity: O(N * k)

Explanation: We're looping through N - k +1 elements in the input array and for each element we're looping through K elements to add up the sums.

Spoiler: We can do better using the sliding window approach!

The first question to ask ourselves is: Where is the inefficiencies?

Well, if you look at each one of the steps where we're calculating the averages, you can see that most of the elements that we're using are the same! The only differences are at the beginning and the end of subarray. (Or if you prefer Linked List terminology, the head and the tail)

Here's a visual representation of the differences and similarities between step 1 and step 2:

As you can see, all the elements in blue are the same. The differences are circled in red.

Let's bring in the sliding window.

Essentially, you need to visualise a sliding window of k elements which slides over one element at each iteration. What I mean by 'slides over' is that we remove an element from the beginning of the sliding window, and we add an element at the end of the sliding window.

Have a look at this beautiful diagram:

Pretty simple right? It's crazy to think that such a small change in the algorithm can lead to such drastic improvements. We've just reduced the space complexity from **O(N * k) → O(N) !**

Here's some code for you:

```
def find_subarray_averages_of_size_k(k: int, arr: List[float]) -> List[float]:
result = []
window_sum, window_start_idx = 0, 0
for window_end_idx in range(len(arr)):
window_sum += arr[window_end_idx] # Add the next element to the sum.
if window_end_idx > k: # We only want to add to the result when the sliding window has reached size k
result.append(window_sum / k)
window_sum -= arr[window_start_idx] # Take away the first element from the sum
window_start_idx += 1 # move the start element of the window ahead.
```

Note: In this case the sliding window was a fixed size. This may not always be the case; you may need to resize based on the constraints of the problem.

Problems to practice:

https://leetcode.com/problems/maximum-average-subarray-i/ (easy)

https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/ (medium)

https://leetcode.com/problems/fruit-into-baskets/ (medium)

https://leetcode.com/problems/max-consecutive-ones-iii/ (hard)

]]>Let's get real here.

I have struggled with motivation since last June (2020).

I had weeks where I had Arnold Schwarzenegger levels of motivation, followed by months of imitating a sloth with no limbs. Let's get it straight; I'm not someone who enjoys doing toy challenges that amount to f*ck all.

So here I am at attempt number n and I'm praying to all the internet gods (Zyzz do you hear me?) that having to write a blog post every day is going to keep me on the golden path to a much sought after role at FAANG, FAAMG or whatever we're calling it nowadays.

"Why put yourself through this?" You may ask.

Its a question that I've often asked myself too (often right after reading the answer to a question which requires dynamic programming). Because in reality, my life is really good; I'm a developer with 5 years experience, with a safe and lucrative job, with plenty of spare time to do the things I love with the people I love. I've hit the jackpot right?

So the answer to that question my friends is that I'm a stubborn pr*ck. I want to get into one of these companies not because of the salary, not to put 'ex-google w*nker' on my linked in profile after I leave, but because I want to prove to myself and all those shitty Leetcode 'programmers' (who think that single letter variable names are OK) that I can.

So thats it for today. Now f*ck off and go do some more Leetcode questions.

Love ya.

]]>