Below are some answers which I came up for the questions asked in the discussion with a doordash tag. Enjoy! I have prepared correct answers (in about a month).
1.) Get Active Time
# Given a sequence of timestamps & actions of a dasher's activity within a day, we would like to know the
# active time of the dasher. Idle time is defined as the dasher has NO delivery at hand. (That means all
# items have been dropped off at this moment and the dasher is just waiting for another pickup) Active time equals
# total time minus idle time.
# Ref: https://leetcode.com/discuss/interview-question/1302606/DoorDash-onsite-interview-(new-question!)
def get_active_time(activity):
pickups = []
dropoffs = []
smallest_pickup = float('inf')
highest_dropoff = float('-inf')
for a in activity:
a = a.split('|')
a_type = a[1].strip()
a_mins = get_mins(a[0].strip())
if a_type == 'pickup':
pickups.append(a_mins)
smallest_pickup = min(smallest_pickup, a_mins)
else:
dropoffs.append(a_mins)
highest_dropoff = max(highest_dropoff, a_mins)
interval = []
for p, d in zip(pickups, dropoffs):
interval.append([p, d])
total_time = highest_dropoff - smallest_pickup
idle_time = 0
for idx in range(len(interval) - 1):
curr = interval[idx]
nxt = interval[idx + 1]
if curr[1] < nxt[0]:
idle_time += nxt[0] - curr[1]
else:
nxt[1] = max(nxt[1], curr[1])
return total_time - idle_time
def get_mins(t):
t = t.split(':')
hrs = int(t[0])
mins = int(t[1][:-2])
if t[1][-2:] == 'pm':
if hrs < 12:
hrs += 12
else:
if hrs == 12:
hrs = 0
return 60*hrs + mins
if __name__ == '__main__':
activity = [
'8:30am | pickup',
'9:10am | dropoff',
'10:20am| pickup',
'12:15pm| pickup',
'12:45pm| dropoff',
'2:25pm | dropoff',
]
idle_time = get_active_time(activity)
print(idle_time)
2.) All Shortest Path
# Ref: https://leetcode.com/discuss/interview-question/1353434/Doordash-Phone-Screen-or-Senior-Software-Engineer-or-July-2021
import collections, heapq
def find_shortest_paths(g_nodes, sources, destinations, weights):
graph = collections.defaultdict(list)
weight_between = {}
for s, d, w in zip(sources, destinations, weights):
graph[s].append(d)
graph[d].append(s)
weight_between[(s, d)] = w
weight_between[(d, s)] = w
parents = collections.defaultdict(set)
dist = collections.defaultdict(lambda: float('inf'))
dist[1] = 0
def relaxation(v, u, w):
if dist[v] > dist[u] + w:
dist[v] = dist[u] + w
parents[v] = {u}
elif dist[v] == dist[u] + w:
parents[v].add(u)
q = [(0, 1)]
relaxed = set()
while q:
_, node = heapq.heappop(q)
for nei in graph[node]:
if nei not in relaxed:
relaxation(nei, node, weight_between[(nei, node)])
heapq.heappush(q, (weight_between[(nei, node)], nei))
relaxed.add(node)
shortest_edges = set()
visited = set()
dfs(g_nodes, parents, visited, shortest_edges)
ans = []
for s, d in zip(sources, destinations):
if (s, d) in shortest_edges or (d, s) in shortest_edges:
ans.append('YES')
else:
ans.append('NO')
print(parents)
print(ans)
def dfs(node, parents, visited, shortest_edges):
if node in visited:
return
visited.add(node)
while parents[node]:
p = parents[node].pop()
shortest_edges.add((node, p))
dfs(p, parents, visited, shortest_edges)
if __name__ == '__main__':
find_shortest_paths(
5,
[1, 2, 3, 4, 5, 1, 5],
[2, 3, 4, 5, 1, 3, 3],
[1, 1, 1, 1, 3, 2, 1]
)
3.) Closest Drivers
# Ref: https://leetcode.com/discuss/interview-question/1293040/Doordash-or-Phone-Screen-or-Software-Engineer-E4-or-Closest-Drivers-to-Restaurant
import random
class Dasher:
def __init__(self, id, lastLocation, rating):
self.id = id
self.lastLocation = lastLocation
self.rating = rating
class Location:
def __init__(self, longitude, latitude):
self.longitude = longitude
self.latitude = latitude
def GetDashers():
loc1 = Location(1, 3)
d1 = Dasher(1, loc1, 4)
loc2 = Location(2, 2)
d2 = Dasher(2, loc2, 4)
loc3 = Location(4, 0)
d3 = Dasher(3, loc3, 4)
loc4 = Location(1, 1)
d4 = Dasher(4, loc4, 5)
loc5 = Location(4, 3)
d5 = Dasher(5, loc1, 2)
loc6 = Location(7, 2)
d6 = Dasher(6, loc6, 4)
loc7 = Location(4, 0)
d7 = Dasher(7, loc3, 4)
loc8 = Location(1, 1)
d8 = Dasher(8, loc4, 3)
return [d1, d2, d3, d4, d5, d6, d7, d8]
def find_k_closest_drivers(k):
dashers = GetDashers()
quick_select(0, len(dashers) - 1, dashers, k)
return dashers[:k]
def quick_select(left, right, dashers, k):
while left <= right:
pivot = random.randint(left, right)
pivot = partition(pivot, left, right, dashers)
if pivot == k:
return
elif pivot < k:
left = pivot + 1
else:
right = pivot - 1
def get_sum_hash(dasher):
loc = dasher.lastLocation
return (loc.longitude ** 2 + loc.latitude ** 2, - dasher.rating)
def partition(pivot, left, right, dashers):
pivot_ele = get_sum_hash(dashers[pivot])
dashers[right], dashers[pivot] = dashers[pivot], dashers[right]
store_idx = left
for idx in range(left, right):
if get_sum_hash(dashers[idx]) < pivot_ele:
dashers[store_idx], dashers[idx] = dashers[idx], dashers[store_idx]
store_idx += 1
dashers[store_idx], dashers[right] = dashers[right], dashers[store_idx]
return store_idx
if __name__ == '__main__':
howmany_dashers = 3
dashers = find_k_closest_drivers(howmany_dashers)
for dasher in dashers:
print(
f'dasher_id: {dasher.id}, rating: {dasher.rating},'
f'location: {dasher.lastLocation.latitude},'
f' {dasher.lastLocation.longitude}'
)
4.) Longest Valid Order
# Find longest valid subarray
# Ex 1: orders = ['P1', 'P1', 'D1'], return ['P1', 'D1']
# Ex 2: orders = ['P1', 'P1', 'D1', 'D1'], return ['P1', 'D1']
# Ref: https://leetcode.com/discuss/interview-question/914113/Longest-valid-orders-path-(Doordash)
def longest_valid_order(orders):
longest_idxs = [0, 0]
for start in range(len(orders)):
for end in range(start + 1, len(orders)):
pickups = set()
deliveries = set()
is_valid = True
for idx in range(start, end + 1):
order_type = orders[idx][0]
order_id = orders[idx][1:]
if order_type == 'P':
if order_id not in pickups and order_id not in deliveries:
pickups.add(order_id)
else:
is_valid = False
break
else:
if order_id in pickups and order_id not in deliveries:
deliveries.add(order_id)
else:
is_valid = False
break
if is_valid and len(pickups) == len(deliveries):
update_logest_idxs(longest_idxs, start, end + 1)
ans = []
for idx in range(longest_idxs[0], longest_idxs[1]):
ans.append(orders[idx])
print(ans)
def update_logest_idxs(longest_idxs, start, end):
if longest_idxs[1] - longest_idxs[0] < end - start:
longest_idxs[0] = start
longest_idxs[1] = end
if __name__ == '__main__':
orders = ['P1', 'P1', 'P2', 'D3', 'P1', 'P2', 'D2', 'D1', 'P4', 'D3', 'D1']
longest_valid_order(orders)
5.) Median finder with followups:
# find running median of a data stream
# case 1: data stream contains only nums btw 0 and 100
# case 2: 99% nums are btw 0 to 100
# Ref: https://leetcode.com/problems/find-median-from-data-stream/
class MedianSearch:
def __init__(self):
self.middle = [0] * 101
self.middle_ct = 0
self.left = []
self.right = []
def add_num(self, num):
if num < 0:
self.edge_insert(self.left, num)
elif num > 100:
self.edge_insert(self.right, num)
else:
self.middle[num] += 1
self.middle_ct += 1
def add_num2(self, num):
self.middle[num] += 1
self.middle_ct += 1
def get_median2(self):
first = second = None
if self.middle_ct % 2 != 0:
first = (self.middle_ct // 2) + 1
else:
first = (self.middle_ct // 2)
second = first + 1
first_val = second_val = None
curr = 0
ith = 0
while ith < len(self.middle):
curr += self.middle[ith]
if curr >= first:
first_val = ith
if second is not None:
if curr >= second:
second_val = first_val
else:
ith += 1
while ith < len(self.middle):
curr += self.middle[ith]
if curr >= second:
second_val = ith
break
ith += 1
break
ith += 1
if second is None:
return first_val
else:
return (first_val + second_val) / 2
def edge_insert(self, arr, num):
arr.append(num)
i = len(arr) - 1
while i > 0 and arr[i - 1] > num:
arr[i - 1], arr[i] = arr[i], arr[i - 1]
i -= 1
def get_median(self):
total_nums = self.middle_ct + len(self.left) + len(self.right)
first = (total_nums // 2) + 1
second = None
if total_nums % 2 == 0:
second = first - 1
first_val = self.find_idx(first)
if second is not None:
second_val = self.find_idx(second)
else:
return first_val
return (first_val + second_val) / 2
def find_idx(self, ith):
if ith <= len(self.left):
return self.left[ith - 1]
elif ith > len(self.left) + self.middle_ct:
return self.right[ith - len(self.left) - self.middle_ct - 1]
else:
curr_ct = len(self.left)
for ith_val in range(0, 101):
if self.middle[ith_val] + curr_ct >= ith:
return ith_val
curr_ct += self.middle[ith_val]
if __name__ == '__main__':
ms = MedianSearch()
input = [
('add_num', 5), ('add_num', 0), ('get_median'), ('add_num', 99),
('add_num', 50), ('add_num', 20), ('get_median'), ('add_num', 19),
('add_num', 5), ('add_num', 95), ('get_median'), ('add_num', 0),
('add_num', 100), ('add_num', 20), ('get_median'), ('add_num', 19),
]
queries = [
"addNum","findMedian","addNum","findMedian","addNum",
"findMedian","addNum","findMedian","addNum","findMedian",
"addNum", "findMedian", "addNum", "findMedian","addNum", "findMedian"
]
values = [[-1],[],[-2],[],[-3],[],[-4],[],[-5],[]]
values = [
[-1],[],[2],[],[3],[],[4],[],[5],[],
[100], [], [33], [], [333], []
]
values = [
[1],[],[-2],[],[-3],[],[4],[],[5],[],
[100], [], [332], [], [133], []
]
# values = [
# [1],[],[2],[],[3],[],[4],[],[5],[],
# [100], [], [33], [], [33], []
# ]
# queries = ["addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian","addNum","findMedian"]
# values = [[155],[],[66],[],[114],[],[0],[],[60],[],[73],[],[109],[],[26],[],[154],[],[0],[],[107],[],[75],[],[9],[],[57],[],[53],[],[6],[],[85],[],[151],[],[12],[],[110],[],[64],[],[103],[],[42],[],[103],[],[126],[],[3],[],[88],[],[142],[],[79],[],[88],[],[147],[],[47],[],[134],[],[27],[],[82],[],[95],[],[26],[],[124],[],[71],[],[79],[],[130],[],[91],[],[131],[],[67],[],[64],[],[16],[],[60],[],[156],[],[9],[],[65],[],[21],[],[66],[],[49],[],[108],[],[80],[],[17],[],[159],[],[24],[],[90],[],[79],[],[31],[],[79],[],[113],[],[39],[],[54],[],[156],[],[139],[],[8],[],[90],[],[19],[],[10],[],[50],[],[89],[],[77],[],[83],[],[13],[],[3],[],[71],[],[52],[],[21],[],[50],[],[120],[],[159],[],[45],[],[22],[],[69],[],[144],[],[158],[],[19],[],[109],[],[52],[],[50],[],[51],[],[62],[],[20],[],[22],[],[71],[],[95],[],[47],[],[12],[],[21],[],[32],[],[17],[],[130],[],[109],[],[8],[],[61],[],[13],[],[48],[],[107],[],[14],[],[122],[],[62],[],[54],[],[70],[],[96],[],[11],[],[141],[],[129],[],[157],[],[136],[],[41],[],[40],[],[78],[],[141],[],[16],[],[137],[],[127],[],[19],[],[70],[],[15],[],[16],[],[65],[],[96],[],[157],[],[111],[],[87],[],[95],[],[52],[],[42],[],[12],[],[60],[],[17],[],[20],[],[63],[],[56],[],[37],[],[129],[],[67],[],[129],[],[106],[],[107],[],[133],[],[80],[],[8],[],[56],[],[72],[],[81],[],[143],[],[90],[],[0],[]]
for i in range(len(queries)):
query = queries[i]
if query == 'addNum':
value = values[i][0]
ms.add_num(value)
else:
print(ms.get_median())
6.) Nearest City
# Ref: https://leetcode.com/discuss/interview-question/1379696/DoorDASH-Onsite
import collections, random
def find_nearest_cities(x_list, y_list, cities, query_cities):
xi = collections.defaultdict(list)
yi = collections.defaultdict(list)
city_cords = {}
for x, y, c in zip(x_list, y_list, cities):
xi[x].append((y, c))
yi[y].append((x, c))
city_cords[c] = (x, y)
for xk in xi.keys():
xi[xk].sort()
for yk in yi.keys():
yi[yk].sort()
ans = []
for q_city in query_cities:
if q_city not in city_cords:
ans.append(None)
x, y = city_cords[q_city]
nearest_city = get_nearest_city(xi, yi, x, y, q_city)
ans.append(nearest_city)
return ans
def get_nearest_city(xi, yi, x, y, q_city):
mins = {
'city': None,
'dist': float('inf'),
}
find_mins(0, len(xi[x]) - 1, xi[x], y, q_city, mins)
find_mins(0, len(yi[y]) - 1, yi[y], x, q_city, mins)
return mins['city']
def find_mins(left, right, axis_n_cities, axis_to_compare, q_city, mins):
while left <= right:
mid = random.randint(left, right)
mid_city = axis_n_cities[mid][1]
mid_axis = axis_n_cities[mid][0]
if mid_city == q_city:
if mid > 0:
mid_city = axis_n_cities[mid - 1][1]
mid_axis = axis_n_cities[mid - 1][0]
mid_dist = abs(axis_to_compare - mid_axis)
update_mins(mid_dist, mid_city, mins)
if mid < len(axis_n_cities) - 1:
mid_city = axis_n_cities[mid + 1][1]
mid_axis = axis_n_cities[mid + 1][0]
mid_dist = abs(axis_to_compare - mid_axis)
update_mins(mid_dist, mid_city, mins)
break
if mid_axis < axis_to_compare:
left = mid + 1
else:
right = mid - 1
def update_mins(mid_dist, mid_city, mins):
if mid_dist < mins['dist']:
mins['dist'] = mid_dist
mins['city'] = mid_city
elif mid_dist == mins['dist']:
mins['city'] = min(mins['city'], mid_city)
if __name__ == '__main__':
cities = ['axx', 'axy', 'az', 'axd', 'aa', 'abc', 'abs']
xs = [0, 1, 2, 4, 5, 0, 1]
ys = [1, 2, 5 ,3, 4, 2, 0]
query_cities = ['axx', 'axy', 'abs']
nearest_cities = find_nearest_cities(xs, ys, cities, query_cities)
print(nearest_cities)
7.) Pickup & Delivery Permutaions
# print all valid order paths e.g.: delivery after pickup given n
def get_all_patterns(n):
picked_up = set()
delivered = set()
patterns = []
pattern = []
find_pattern(picked_up, delivered, pattern, patterns, n)
return patterns
def find_pattern(picked_up, delivered, pattern, patterns, n):
if len(pattern) == n * 2:
patterns.append('->'.join(pattern))
else:
for task in range(1, n + 1):
pickup = 'P' + str(task)
delivery = 'D' + str(task)
if pickup not in picked_up:
picked_up.add(pickup)
pattern.append(pickup)
find_pattern(picked_up, delivered, pattern, patterns, n)
pattern.pop()
picked_up.remove(pickup)
if pickup in picked_up and delivery not in delivered:
delivered.add(delivery)
pattern.append(delivery)
find_pattern(picked_up, delivered, pattern, patterns, n)
pattern.pop()
delivered.remove(delivery)
if __name__ == '__main__':
print(get_all_patterns(3))
# Time:
# O((N) ^ 2*N)
# Space:
# O(2* N) => O(N) to hold a single pattern
# 2n -1 5 3 1
# for p: N! for d => p1, p2..pn-1..pn
# 2n-1 2n - 3 ..... 3, 1
# p1 p2....p3..pn
8.) Stream last x max
# Given a streaming data of the form (timestamp, value),
# find the maximum value in the stream in the last X seconds.
# Assume time is monotonically increasing.
# Assume time is in the order of seconds.
# max_value() function finds the max in the last X seconds.
# Ref: https://leetcode.com/discuss/interview-question/1302614/DoorDash-Onsite-Interview-(new-question-again!)
import collections
class StreamProcessor:
def __init__(self, x):
self.x = x
self.deque = collections.deque()
def set_value(self, t, v):
while self.deque and t - self.deque[0][0] > self.x:
self.deque.popleft()
while self.deque and self.deque[-1][1] < v:
self.deque.pop()
self.deque.append((t, v))
def max_value(self, cur_t): # this will be always current time
while self.deque and cur_t - self.deque[0][0] > self.x:
self.deque.popleft()
if not self.deque:
return -1
return self.deque[0][1]
if __name__ == '__main__':
sp = StreamProcessor(5)
sp.set_value(0, 5)
sp.set_value(1, 6)
sp.set_value(2, 4)
sp.set_value(5, 5)
sp.set_value(9, 19)
sp.set_value(15, 4)
sp.set_value(16, 25)
sp.set_value(19, 6)
sp.set_value(20, 4)
print(sp.max_value(22))
9.) Time increament
# Ref: https://leetcode.com/discuss/interview-question/1387937/Doordash-new-Q
def get_increments(times):
days = {
'mon': 1,
'tue': 2,
'wed': 3,
'thu': 4,
'fri': 5,
'sat': 6,
'sun': 7,
}
start = clean_time(times[0], days)
end = clean_time(times[1], days, True)
end_borders = set()
for minutes in range(1, 6):
end_borders.add(add_delta(end, minutes))
ans = []
curr = start
while curr not in end_borders:
ans.append(int(curr))
curr = add_delta(curr, 5)
print(ans)
def clean_time(t, days, is_end=False):
t = t.split()
day = days[t[0]]
hrs_mins = t[1].split(':')
hrs = int(hrs_mins[0])
mins = int(hrs_mins[1])
if t[2] == 'pm':
if hrs < 12:
hrs += 1
else:
if hrs == 12:
hrs = 0
nearest = mins % 5
if nearest < 3:
mins -= nearest
else:
if not is_end:
return add_delta(
str(day) + get_str(hrs) + get_str(mins), 5 - nearest
)
return str(day) + get_str(hrs) + get_str(mins)
def add_delta(t, delta_mins):
day = int(t[0])
hrs = int(t[1:3])
mins = int(t[3:])
if mins + delta_mins < 60:
mins += delta_mins
return str(day) + get_str(hrs) + get_str(mins)
else:
mins = 0
if hrs + 1 < 24:
hrs += 1
return str(day) + get_str(hrs) + get_str(mins)
else:
hrs = 0
if day + 1 < 8:
day += 1
return str(day) + get_str(hrs) + get_str(mins)
else:
day = 1
return str(day) + get_str(hrs) + get_str(mins)
def get_str(i):
if i < 10:
return '0' + str(i)
return str(i)
if __name__ == '__main__':
times = ['tue 00:29 am', 'mon 11:56 pm']
get_increments(times)
10.) Valid order
# given an pickup delivery, return whether it is valid or not
def validate(pattern):
picked_up = set()
delivered = set()
for task in pattern:
task_type = task[0]
task_id = task[1:]
if task_type == 'P':
if task_id not in picked_up:
picked_up.add(task_id)
else:
return False
else:
if task_id not in delivered and task_id in picked_up:
delivered.add(task_id)
else:
return False
return len(picked_up) == len(delivered)
if __name__ == '__main__':
pattern = [
'P1', 'P3', 'P2', 'D3', 'P4', 'P404', 'D2', 'D1', 'D404', 'D4',
'P33', 'D33',
]
print(validate(pattern))
Prepare these ones and LC doordash tagged.
(Please go to Ref: link too see question statement)