You have a list arr of all integers in the range [1, n] sorted in a strictly increasing order. Apply the following algorithm on arr:
Starting from left to right, remove the first number and every other number afterward until you reach the end of the list.
Repeat the previous step again, but this time from right to left, remove the rightmost number and every other number from the remaining numbers.
Keep repeating the steps again, alternating left to right and right to left, until a single number remains.
Given the integer n, return the last number that remains in arr.
Example 1:
Input: n = 9
Output: 6
Explanation:
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
arr = [2, 4, 6, 8]
arr = [2, 6]
arr = [6]
Example 2:
Input: n = 1
Output: 1
=> Couldn't solve
def lastRemaining(self, n: int) -> int:
if n <= 2:
return n
if n == 3:
return 2
all = set()
if (n // 2) % 2 == 0:
index = 1
else:
index = 3
while index < n:
all.add(index)
index += 4
to_right = 1
while len(all) != 1:
if to_right or len(all) % 2 == 1:
start = 0
else:
start = 1
i = 0
temp_list = sorted(all)
for var in temp_list:
if i % 2 == start:
all.remove(var)
i += 1
to_right = (to_right + 1) % 2
return list(all)[0] + 1
Memory Limit Exceeded
3375/3377 cases passed (N/A)
Testcase
100000000
Expected Answer
32896342
To solve this problem efficiently without hitting a time limit, you can adopt a mathematical approach instead of simulating the entire process, which can become inefficient for large values of n
. The idea is to observe patterns and rules that emerge as you remove elements from the array according to the algorithm's rules.
Here's a more efficient strategy:
Here's how you could implement this approach:
n
.This process continues until the remaining length of the list is 1.
Let's implement this improved solution:
def lastRemaining(n: int) -> int:
start = 1 # The first number in the list.
step = 1 # The difference between consecutive numbers in the list.
remaining = n # The remaining count of numbers in the list.
left_to_right = True # Direction of removal.
while remaining > 1:
if left_to_right or remaining % 2 == 1:
# If we are moving left to right or the count of remaining numbers is odd,
# the start changes because the first or last (in case of odd during right to left)
# number will be removed.
start += step
# After each pass (both directions), the difference between the remaining numbers doubles.
step *= 2
# Halve the count of remaining numbers after each pass.
remaining //= 2
# Toggle the direction for the next pass.
left_to_right = not left_to_right
return start
This solution works in (O(\log n)) time complexity, which is significantly faster than the simulated approach, especially for large n
. It avoids the time limit issue by not explicitly removing each element and instead mathematically calculating the position of the remaining number after all removals have been performed.