Linear search is the most basic way to find something in a list: look at each element in turn until you hit a match (or run out). It needs no sorting, no extra structure — just a single left-to-right pass. The sentinel variant is a small optimization that removes one comparison per step by planting the target in an extra slot at the end.
Core idea. Walk the array one cell at a time, comparing each value to the target x. Return the
index of the first match, or -1 if you reach the end. For a = [4, 2, 7, 1, 9] and x = 7, the
answer is index 2.
Every iteration of a plain linear search actually does two checks: "am I still inside the array?" (the loop bound) and "is this the value I want?". The sentinel trick collapses that to just one. By copying the key into an extra cell at the very end, you guarantee the search always finds a match, so the loop can stop checking the bound and only compares values.
Intuition
Picture flipping through an unsorted stack of cards looking for the seven of hearts. You have no choice but to turn them over one by one — there is no shortcut, because the cards are in no particular order. The moment you see it, you stop. If you reach the bottom without finding it, it is not in the stack. That is linear search: simple, always correct, and O(n).
The sentinel idea is like sliding a guaranteed seven of hearts under the bottom of the stack before you start. Now you can flip cards confidently without ever checking "is this the last card?" — you know a seven is coming. When you stop, you just ask one question: did I find it before the bottom (a real card) or was it the fake one I planted (meaning it was not really there)?
Walk through it
Step through the animation on the right. Phase 1 is plain linear search. The i pointer starts at index 0 and scans right. a[0] = 4 is not 7, so the cell turns visited and i advances. Same for a[1] = 2. At a[2] = 7 the value matches — the cell lights up as the hit and we return index 2.
Phase 2 is the sentinel variant. We reveal an extra cell after the array and copy the key 7 into it (the pivot-colored slot). Now i walks again, but this time the loop does no i < n check — it just compares values until one equals 7. It stops at index 2 exactly as before. Finally we restore the original last value and run a single test: is the stop index i = 2 less than n = 5? Yes, so it is a genuine match. Had the walk run all the way onto the sentinel (i == n), that would have meant the key was absent and we would return -1.
The code, line by line
def linear_search(a, x):
for i in range(len(a)):
if a[i] == x:
return i
return -1
def sentinel_search(a, x):
n = len(a)
last = a[n - 1]
a[n - 1] = x # plant the key
i = 0
while a[i] != x: # no i < n check needed
i += 1
a[n - 1] = last # restore the array
return i if (i < n - 1 or last == x) else -1linear_searchis the baseline: theforloop both advancesiand bounds it, and line 3 is the value comparison. First match returns immediately; falling off the end returns-1.last = a[n - 1]saves the real final value before we overwrite it — we must put it back so the caller's array is unchanged.a[n - 1] = xplants the key in the last slot. This makes the upcomingwhileguaranteed to terminate.- Line 12,
while a[i] != x, is the whole point: it compares only values, with no bound check, because the planted key ensures a match always exists. a[n - 1] = lastrestores the array to its original contents.- The final line decides real vs. fake hit. If
i < n - 1, the match was strictly before the sentinel slot, so it is real. Ifi == n - 1, it could be the planted key or a genuine match in the last position —last == xdisambiguates. Anything else means not found, so return-1.
Complexity
| Case | Time | Notes |
|---|---|---|
| Best | O(1) (fast) | target is the first element |
| Average / Worst | O(n) (moderate) | scan up to every element; sentinel saves a constant per step, not a factor |
| Space | O(1) (fast) | a couple of variables; the sentinel reuses the array slot, no new memory |
O(1) (fast)Both versions are O(n): the sentinel does not change the asymptotic cost. What it removes is one comparison (the i < n bound) on every iteration, which can be a measurable constant-factor win in a tight inner loop on large arrays. It is a classic micro-optimization, not an algorithmic one.
When to use / pitfalls
Reach for linear search whenever the data is unsorted or tiny, or when you only scan once and
sorting first would not pay off. If the array is sorted, prefer binary search for O(log n). The
sentinel trick is worth mentioning as a way to shave a comparison per step, but say plainly that it
keeps the same O(n) bound — interviewers want to see you separate constant factors from complexity.
The sentinel version mutates the array by overwriting the last cell. Always restore it (the
a[n - 1] = last line), or a caller that reuses the array gets a corrupted final element. Also handle
the edge case where the real match is in the last position: the final last == x test exists precisely
so a genuine hit at index n - 1 is not mistaken for the planted sentinel. An empty array needs a guard
too, since a[n - 1] would fail.
Practice
Searching a = [4, 2, 7, 1, 9] for x = 1 with sentinel search: at what index does the while-loop stop, and is it a real hit?
1. What is the time complexity of linear search in the worst case?
2. What does the sentinel trick actually save?
3. Why must sentinel search restore a[n - 1] before returning?
4. For a = [4, 2, 7, 1, 9], what does linear_search(a, 8) return?