Skip to content

Commit 3ef4feb

Browse files
committed
convert all arrays notebooks to md
1 parent 4128afe commit 3ef4feb

19 files changed

+1667
-0
lines changed

arrays/first_duplicate.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
## Problem
2+
Find the first duplicate value in an array of positive integers. Each integer value in the array is less or equal to the length of the array.
3+
4+
Sample input: [1, 2, 1, 3] (Notice the length of array is 3, so the largest value is 3)
5+
6+
Sample output: 1
7+
8+
Try finding a solution that runs in constant space.
9+
10+
11+
```python
12+
## solution (O(n) time | O(n) space)
13+
def first_dup(array):
14+
unique = set()
15+
for i in array:
16+
if i in unique:
17+
return i
18+
unique.add(i)
19+
return -1
20+
21+
```
22+
23+
24+
```python
25+
first_dup([1, 2, 1, 3, 2])
26+
```
27+
28+
29+
30+
31+
1
32+
33+
34+
35+
### Optimal Approach
36+
We can take each element's value minus 1 and use it as an index. Where the index will fall in the array, we'll set that value to -ve. As we loop through the array,
37+
if we end up at an element that is already set to -ve, we know we've found a duplicate.
38+
39+
40+
41+
```python
42+
def first_duplicate(array):
43+
"""Complexity: O(n) time | O(1) space"""
44+
45+
i = 0
46+
while i < len(array):
47+
val = abs(array[i]) - 1
48+
# if we find a negative number, it's our first duplicate
49+
if array[val] < 0:
50+
return abs(array[val])
51+
else:
52+
# make the current value negative
53+
array[val] = -array[val]
54+
# increment i
55+
i += 1
56+
return -1
57+
```
58+
59+
60+
```python
61+
first_duplicate([ 2, 1, 3, 4, 7, 6, 7])
62+
```
63+
64+
65+
66+
67+
7
68+
69+

arrays/k_sorted_lists.md

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Finding The Smallest Interval of K-sorted Lists.
2+
3+
Given K sorted lists of integers, return the **smallest interval** (inclusive) that contains at least one element from each least.
4+
5+
For example, given:
6+
7+
```python
8+
[[0, 1, 4, 17, 20, 25, 31],
9+
[5, 6, 10],
10+
[0, 3, 7, 8, 12]]
11+
```
12+
The smallest range here is [3,5], since it contains:
13+
* 4 from first list,
14+
* 5 from the second list (inclusive),
15+
* 3 from the third list (inclusive)
16+
17+
18+
19+
## Naive Solution
20+
The brute force solution would be to compare every pair of elements in the lists and consider their intervals. After finding this interval, traverse every list to make sure that there is at least one element from each list in the interval.
21+
22+
In order to find the smallest such interval, we need to store the smallest seen so far, and update if we see a smaller interval.
23+
24+
This would be an expensive O(N^3), where N is the total number of elements in all K lists. On the bright side, this solution uses O(1) memory, since it only needs to store the current smallest interval.
25+
26+
27+
## Solution 1: K-pointers
28+
Since the K lists are all sorted, this suggest iterating from the beginning (smallest elements) to end (largest elements).
29+
30+
&nbsp;
31+
32+
Imagine comparing the minimum values of all the arrays: In our example above, the first values will be **[0, 5, 0]**. And the interval would be the minimum and maximum of these values: **[0, 5]**.
33+
34+
&nbsp;
35+
36+
This is one such interval but we aren't sure if it's the smallest. So we must keep looking. We must step along by increasing the minimum. In this case, the next interval we should consider is **[1, 5]**
37+
38+
Let's translate this process into an alogirithm:
39+
40+
1. Initialize K pointers, one for each list, pointing the minimum element in the list. (index 0 -- because they are sorted).
41+
2. Initialize variables to track the right and left boundaries of the interval.
42+
3. Find the pointer pointing to the **minimum** and the pointer pointing to the **maximum** of all the values pointed to. This becomes your **tracked interval**.
43+
4. If the new interval is smaller than the previously tracked interval, update the tracked interval to be this new interval.
44+
5. Increment the minimum value pointer. After incrementing this pointer, it may not point to a minimum value anymore.
45+
6. Repeat steps 3 -5 until we get to the end of one of the lists.
46+
7. Return the tracked interval (by now, it's the smallest)
47+
48+
49+
```python
50+
def smallest_interval(k_arrays):
51+
# step 1: init K pointers
52+
k_pointers = [0] * len(k_arrays) # evaluates to [0, 0, 0]
53+
# step 2: init the left, right boundaries of the interval
54+
interval = float('-inf'), float('inf')
55+
56+
while True:
57+
# initialize local max and min, for updating tracked interval.
58+
local_max = float('-inf')
59+
local_min = float('inf')
60+
min_index = -1
61+
reached_end = False
62+
63+
# step 3: find out which is the max, min to make the interval
64+
for i in range(len(k_pointers)):
65+
66+
# first, check if we've reached the end of one of the K-lists
67+
if k_pointers[i] >= len(k_arrays[i]):
68+
reached_end = True
69+
break
70+
71+
if k_arrays[i][k_pointers[i]] > local_max:
72+
local_max = k_arrays[i][k_pointers[i]]
73+
74+
if k_arrays[i][k_pointers[i]] < local_min:
75+
local_min = k_arrays[i][k_pointers[i]]
76+
# save the index of minimum value, to be used later for incrementing
77+
min_index = i
78+
79+
# if we've reached the end of any list,
80+
# we've already found the smallest interval
81+
if reached_end:
82+
break
83+
84+
# step 4: update, if the new interval is < previous interval
85+
if local_max - local_min < interval[1] - interval[0]:
86+
interval = local_min, local_max
87+
88+
# step 5: increment the minimum value pointer
89+
k_pointers[min_index] = k_pointers[min_index] + 1
90+
91+
return interval
92+
93+
```
94+
95+
96+
```python
97+
# let's test it out
98+
k_arrays = [[0, 1, 4, 17, 20, 25, 31],
99+
[5, 6, 10, 13],
100+
[0, 3, 7, 8, 12]]
101+
102+
smallest_interval(k_arrays)
103+
```
104+
105+
106+
107+
108+
(3, 5)
109+
110+
111+
112+
This code runs in O(K * N) time where:
113+
114+
K = number of lists,
115+
116+
and N = total number of elements in all the lists.
117+
118+
The space compexity is O(K), since we are storing a K length array of pointers.
119+
120+
## Solution 2: Min-Heap
121+
We can use a heap to simplify much of the work in the for loop.
122+
123+
If we used a heap instead of an array of pointers to track the values we are currently looking at, we would be able to find the local minimum in O(1) time.
124+
125+
However, we still need to know which list the local minimum is from: for this, we can make use of Python's tuple.
126+
127+
&nbsp;
128+
129+
> The min-heap is a heap where the first element is guaranteed to be minimum of all elements in the heap.
130+
131+
Consider a min-heap consisting of tuples holding the following info:
132+
```
133+
( value, which list it is from, index of the value in that list )
134+
```
135+
136+
We can adapt the algorithm to use the heap as follows:
137+
138+
1. Initialize the heap of size K, with all the tuples being: (first value of list, the list it is from, index 0).
139+
2. Initialize variables to track the right and left boundaries of the interval
140+
3. Initialize the `local_max` variable to be the max of the first set of values. Since we are using a min-heap, there is no easy way to retrieve the maximum value, so we will need to track it.
141+
4. Pop an element from the top of the heap. The element contains the `local_min`, the list it is from, and index within that list.
142+
5. Compare the new range `(local maximum - local minimum)` and update the current tracked interval if necessary.
143+
6. Increment the `local min`s index, and read the value.
144+
7. If the value is larger than the `local_max`, update the `local_max`. This sets ut up so that the next iteration has an updated version of `local_max`.
145+
8. Create a heap element using the new value, and insert it into the heap.
146+
9. Repeat steps 4-8 until we've exhaused the list.
147+
148+
149+
150+
```python
151+
import heapq
152+
153+
def smallest_interval(k_arrays):
154+
155+
# initialize heap,
156+
# each tuple contains (value, the list it is from, value's index)
157+
heap = [(row[0], i, 0) for i, row in enumerate(k_arrays)]
158+
heapq.heapify(heap)
159+
160+
# initialize local maximum and interval
161+
local_max = max(row[0] for row in k_arrays)
162+
interval = [float('-inf'), float('inf')]
163+
164+
while heap:
165+
# pop local minimum from the heap
166+
local_min, k_list, min_index = heapq.heappop(heap)
167+
# if the new interval is smaller that tracked interval, update it
168+
if local_max - local_min < interval[1] - interval[0]:
169+
interval = [local_min, local_max]
170+
171+
# if we've reached the end of the list, break
172+
if min_index == len(k_arrays[k_list]) - 1:
173+
return interval
174+
175+
# increment the min index and recalculate local max
176+
min_index += 1
177+
next_val = (k_arrays[k_list][min_index])
178+
local_max = max(next_val, local_max)
179+
180+
# push new values into heap
181+
# (next value, list it is from, the value's index in its list)
182+
heapq.heappush(heap, (next_val, k_list, min_index))
183+
184+
```
185+
186+
187+
```python
188+
smallest_interval(k_arrays)
189+
```
190+
191+
192+
193+
194+
[3, 5]
195+
196+
197+
198+
Popping and pushing an element from the heap takes O(log(N)) time, where N is the number of elements in the heap.
199+
200+
Since our heap will be maximum size K, and in the worst case, we'll need to iterate for every value in the lists, our total time complexity is O(N log K), where N is the total amount of elements in the lists.
201+
202+
Our space complexity is O(K), as we are storing at most one element per list in the array.

arrays/longest_peak_in_array.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
### Longest Peak
2+
Write a function that takes in an array and returns the length of the longest peak in the array.
3+
4+
A peak is defined as adjacent elements that are strictly increasing towards a peak tip, then scrictly decreasing.
5+
6+
For instance, the array values `1, 2, 5, 3, 0` have a peak, while `10, 0, 4`. Similarly, `1, 2, 3` isn't a peak beacuase there aren't any integers decreasing after 3.
7+
8+
Note: There can exist more than one peak in the array. Return the longest one.
9+
10+
Sample input:
11+
```
12+
[11, 3, 6, 4, 0, 10, 6, 5, -3, -1, 10]
13+
```
14+
15+
Sample output:
16+
```
17+
6
18+
```
19+
20+
21+
```python
22+
def longest_peak(A):
23+
"""
24+
Complexity: O(1) space, O(n) time, where n is the total number of elements in the array A.
25+
"""
26+
27+
longest_peak_length = 0
28+
# start from the second elem in array (i = 1)
29+
i = 1
30+
while i < len(A) - 1:
31+
# check each element for peakness
32+
peak = array[i - 1] < array[i] and array[i] > array[i + 1]
33+
if not peak:
34+
i += 1
35+
continue
36+
# spread outwards in left direction to find peak start.
37+
left_index = i - 2
38+
while left_index >= 0 and A[left_index] < A[left_index + 1]:
39+
left_index -= 1
40+
# spread outwards in right direction to find peak stop.
41+
right_index = i + 2
42+
while right_index < len(A) and A[right_index] < A[right_index - 1]:
43+
right_index += 1
44+
45+
current_peak_length = right_index - left_index - 1
46+
longest_peak_length = max(current_peak_length, longest_peak_length)
47+
i = right_index
48+
return longest_peak_length
49+
```
50+
51+
52+
```python
53+
array = [1, 2, 3, 3, 4, 0, 10, 6, 5, -1, -3, 2, 3]
54+
longest_peak(array)
55+
```
56+
57+
58+
59+
60+
6
61+
62+

0 commit comments

Comments
 (0)