Description
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
.
Examples:
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
Solution in Python
Key Observations:
- Alternating Elimination Direction:
- Each round of elimination alternates between left-to-right and right-to-left.
- Starting from the left, we eliminate the first element and every other element. This effectively doubles the step size (spacing between numbers).
- When going from right-to-left, the elimination pattern is the same (every other element), but starts from the end.
- Updating Key Variables:
- Head: The first remaining element in the list.
- Step: The interval or spacing between numbers in the current sequence after each elimination.
- Direction: Left-to-right or right-to-left.
- Remaining Count: How many elements remain in the current iteration.
- Impact of Each Round on Variables:
- Left-to-right: Always moves the head to the next element (
head += step
), as the first element is always removed. - Right-to-left: Moves the head to the next element only if there’s an odd number of remaining elements (since the last element is removed).
- Doubling the Step: Each round effectively doubles the step because we eliminate every other element.
- Left-to-right: Always moves the head to the next element (
- Termination:
- The process terminates when only one element remains.
Python
class Solution:
def lastRemaining(self, n: int) -> int:
# Initialize variables
head = 1 # Starting element
step = 1 # Initial step size
left_to_right = True # Start elimination from left-to-right
remaining = n # Number of elements remaining
# Loop until only one element remains
while remaining > 1:
# If we're moving left-to-right, or if there is an odd number of remaining elements
# (in right-to-left, odd remaining means the head will shift)
if left_to_right or remaining % 2 == 1:
head += step
# Double the step for the next round as every other element is removed
step *= 2
# Toggle the direction
left_to_right = not left_to_right
# Halve the remaining elements as half are removed in each round
remaining //= 2
# The last remaining number is stored in head
return head
Explanation of the Code
- Initialize Variables:
head
is set to 1 because the list starts at 1.step
starts at 1, representing the initial spacing between numbers.left_to_right
isTrue
, as we begin eliminating from left to right.remaining
is initialized ton
, representing the count of elements initially.
- Loop Execution:
- The loop continues until only one element remains (
remaining > 1
). - If we are eliminating left-to-right, or if the number of elements is odd when eliminating right-to-left, we update the head (
head += step
). - We double the step after each round (
step *= 2
) to reflect the increasing gap between numbers. - We toggle the direction (
left_to_right = not left_to_right
) to alternate between left-to-right and right-to-left eliminations. - The
remaining
count is halved each time because half of the elements are removed in each round.
- The loop continues until only one element remains (
- Return the Last Remaining Number:
- When the loop finishes,
head
will contain the last remaining element.
- When the loop finishes,
Solution in C++
C++
class Solution {
public:
int lastRemaining(int n) {
// Initialize variables:
// - 'left_to_right' indicates the current direction of elimination.
// - 'remaining' keeps track of the number of elements still in the list.
// - 'step' is the interval between remaining elements after each round.
// - 'head' keeps track of the starting element in the list after each round.
bool left_to_right = true;
int remaining = n;
int step = 1;
int head = 1;
// Loop until only one element remains
while (remaining > 1) {
// If we are moving left to right or if remaining elements are odd,
// the head will move forward by 'step' amount.
if (left_to_right || remaining % 2 == 1) {
head += step;
}
// After each round, the number of remaining elements is halved,
// and the step size doubles.
remaining /= 2;
step *= 2;
left_to_right = !left_to_right; // Alternate direction
}
// The last remaining element is stored in 'head'
return head;
}
};
Solution in Javascript
JavaScript
var lastRemaining = function(n) {
// Initialize the first number in the sequence
let first = 1;
// Initialize the step size, which doubles every round
let step = 1;
// Track whether we are moving left to right (true) or right to left (false)
let leftToRight = true;
// Continue until there is only one element remaining
while (n > 1) {
// If moving left to right or if n is odd, update `first`
if (leftToRight || n % 2 === 1) {
first += step;
}
// Move to the next round with a doubled step and switch direction
step *= 2;
n = Math.floor(n / 2);
leftToRight = !leftToRight;
}
// `first` will hold the last remaining number
return first;
};
Solution in Java
Java
class Solution {
public int lastRemaining(int n) {
// Initialize variables for the first element, step size, and number of elements left
int start = 1; // Start of the list in each iteration
int step = 1; // Step size between elements after each round
int remaining = n; // Number of remaining elements in the list
boolean leftToRight = true; // Direction flag, starts with left-to-right pass
// Continue the elimination until only one element remains
while (remaining > 1) {
// If we're moving from left-to-right, or if the remaining count is odd when moving right-to-left,
// we must update the starting element because the first element is always removed.
if (leftToRight || remaining % 2 == 1) {
start += step;
}
// Update remaining number of elements and step size
remaining /= 2;
step *= 2;
// Toggle the direction for the next pass
leftToRight = !leftToRight;
}
// 'start' is the last remaining element
return start;
}
}