Problem overview
The method dropEnds gets a list of integers nums and returns a list with every number except the first and the last. The numbers in between keep their order. The list always has at least two numbers, and when it has exactly two, the result is an empty list.
Examples:
[4, 8, 15, 16, 23]gives[8, 15, 16]. The 4 and the 23 are the ends.[9, 0, 9]gives[0]. Both 9s are ends, so only the 0 stays.[1, 2]gives[], because nothing sits between the ends.
Approach: start at 1, stop before the last
The wanted items start at index 1, because index 0 is the first number. They run up to, but not including, the last index. Python slices stop just before their end index, and a negative index counts from the end, so -1 is the last item. That makes nums[1:-1] exactly "from index 1 up to the last item, which is left out".
The slice also handles the two-number case without any extra code. With [1, 2], index 1 is the last item, so the slice starts at the item it must stop before and holds nothing: an empty list.
Here is the first example traced by index. The list [4, 8, 15, 16, 23] has indexes 0 to 4, and -1 is the same position as index 4. The slice starts at index 1 (the 8) and runs up to, but not including, index 4 (the 23). It keeps the items at indexes 1, 2 and 3: 8, 15 and 16. The advantage of -1 is that it works for any length. You never have to compute the last index yourself, so one expression fits lists of 2 numbers and of 10.
Step by step
- Slice
nums[1:-1]and store it inmiddle. The start of 1 skips the first number, and the end of-1skips the last. - Return
middle, a new list. The originalnumsis not changed.
The solution in Python
class Solution:
def dropEnds(self, nums: List[int]) -> List[int]:
middle = nums[1:-1]
return middle
On the three examples it prints:
[8, 15, 16]
[0]
[]
A negative index in a slice is not an error. In nums[1:-1] the -1 is turned into len(nums) - 1, so the slice is the same as nums[1:len(nums) - 1].
Another way: a loop
A loop can build the same list by visiting only the indexes that are not the ends:
def drop_loop(nums):
result = []
for i in range(1, len(nums) - 1):
result.append(nums[i])
return result
range(1, len(nums) - 1) starts at index 1 and stops before the last index. For [4, 8, 15, 16, 23] the indexes are 1, 2 and 3. For [1, 2] the range is range(1, 1), which is empty, so the result is []. The loop returns the same lists as the slice on all three examples, but the slice says the same thing in one line.
Edge cases and common mistakes
- Writing
nums[1:-2]. It drops the last two numbers. For the first example it gives[8, 15]. - Writing
nums[1:]ornums[:-1]. Each drops only one end. For the first example they give[8, 15, 16, 23]and[4, 8, 15, 16]. - Forgetting the two-number case. Many beginners add an
if len(nums) == 2branch. It isn't needed: the slice returns an empty list by itself. - Removing items with
pop.nums.pop(0)andnums.pop()would work, but they change the list the caller passed in. A slice leaves it alone.
Testing
Besides the three examples, try a list of two equal numbers such as [7, 7], which should give [], and a four-number list, whose result should have exactly two numbers.
