You are given logs describing the execution of n
functions on a single-threaded CPU. Each function has a unique ID from 0 to n-1
. The logs are in the format "{function_id}:{type}:{timestamp}"
, where type
is either "start" or "end".
For example, the log "0:start:3" means function 0 started at timestamp 3, and "1:end:2" means function 1 ended at timestamp 2. A function's exclusive time is the sum of execution times for all its calls.
Your Task:
Write a function that takes n
and a list of logs and returns an array where the value at index i
represents the exclusive time for function i
.
Example:
n = 2
logs = ["0:start:0","1:start:2","1:end:5","0:end:6"]
output = [3, 4]
Explanation:
Constraints:
1 <= n <= 100
2 <= logs.length <= 500
0 <= function_id < n
0 <= timestamp <= 10^9
How would you approach this problem, and what would be the time and space complexity of your solution?
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
We need to figure out how much time each function spends running, given logs of when they start and stop. The brute force way is like simulating the entire execution, second by second, and tracking which function is running at each moment. It's like watching the whole process unfold.
Here's how the algorithm would work step-by-step:
def exclusive_time_brute_force(number_of_functions, logs):
function_execution_times = [0] * number_of_functions
current_time = 0
log_entries = []
for log in logs:
function_id, event_type, timestamp = log.split(":")
log_entries.append((int(function_id), event_type, int(timestamp)))
while True:
current_function = -1
# Find the currently running function.
for function_id in range(number_of_functions):
start_found = False
end_found = False
start_time = -1
end_time = -1
for function_id_log, event_type, timestamp in log_entries:
if function_id_log == function_id:
if event_type == "start" and timestamp <= current_time:
start_found = True
start_time = timestamp
if event_type == "end" and timestamp >= current_time:
end_found = True
end_time = timestamp
if start_found and not end_found:
current_function = function_id
break
if current_function != -1:
function_execution_times[current_function] += 1
# Check if all logs have been processed to stop the simulation.
all_logs_processed = True
max_time = -1
for function_id, event_type, timestamp in log_entries:
max_time = max(max_time, timestamp)
if current_time > max_time:
all_logs_processed = True
else:
all_logs_processed = False
if all_logs_processed and current_function == -1:
break
current_time += 1
return function_execution_times
To efficiently calculate the exclusive time of functions, we can use a stack to track the currently executing function. When a function starts, we push it onto the stack, and when it ends, we pop it. The exclusive time is calculated by tracking the durations between start and end logs, taking into account nested function calls.
Here's how the algorithm would work step-by-step:
def get_exclusive_time(number_of_functions, logs):
exclusive_times = [0] * number_of_functions
stack_of_functions = []
previous_time = 0
for log in logs:
function_id, operation, timestamp = log.split(':')
function_id = int(function_id)
timestamp = int(timestamp)
if operation == 'start':
# Account for time elapsed in the previous function
if stack_of_functions:
exclusive_times[stack_of_functions[-1]] += timestamp - previous_time
stack_of_functions.append(function_id)
previous_time = timestamp
else:
# Accumulate time for current function and update the time
exclusive_times[function_id] += timestamp - previous_time + 1
stack_of_functions.pop()
previous_time = timestamp + 1
# Update previous_time after end
return exclusive_times
Case | How to Handle |
---|---|
Null or empty logs array | Return an empty list or an array of zeros of appropriate size to indicate no execution time. |
Logs array with only 'start' events and no corresponding 'end' events. | Handle incomplete function calls by ignoring the 'start' events at the end or assuming they end at the end of the entire log. |
Logs array with only 'end' events and no corresponding 'start' events. | Ignore such 'end' events since they are invalid without a matching 'start'. |
Nested function calls that overlap (e.g., function A starts, then function B starts, then function A ends, then function B ends). | The stack-based approach should correctly handle overlapping calls by assigning exclusive time based on the current top of the stack. |
Number of functions (n) is very large, potentially leading to large result array allocation. | Ensure memory is allocated dynamically and efficiently; consider a more memory-efficient data structure if scaling becomes a problem. |
Log timestamps are not strictly increasing. | Sort the log entries by timestamp before processing to ensure correct time calculation, or throw an error if non-increasing logs are considered invalid. |
Integer overflow in calculating execution time differences. | Use long integers or appropriate data types to avoid integer overflow when calculating differences in timestamps. |
Function calls that start and end at the same timestamp. | Handle zero-duration calls correctly by ensuring the time difference calculation is accurate (end_time - start_time + 1 when appropriate). |