Linear search
In short: Linear search checks the elements one by one until it finds the one it wants or reaches the end: O(n) comparisons, and it works on an unsorted list.
Problem
The warehouse scanner
In the warehouse the products are recorded in a list in the order they arrived: flour from Monday, sugar from Tuesday, oil, rice. A worker scans a box, and the program must find the product by its barcode:
List<Product> shelf = List.of( new Product("3800200101112", "Flour"), new Product("3800200104403", "Sugar"), new Product("3800200107251", "Oil"), new Product("3800200102690", "Rice"), new Product("3800200109958", "Salt"), new Product("3800200105837", "Coffee"));The barcodes are not sorted. You cannot open the list in the middle and say "the rice is further on": the next barcode can be anything. How do you find the product, and what does it cost when there are 50 000 products and 2000 scans a day?
The idea
You look at every box
Since the order does not help, one thing is left: you start at the first box and compare its barcode with the one you look for. If it matches, you are done: you return the place. If not, you move to the next one. If the boxes run out, the product is not there.
This is linear search: you go through the elements one by one until you find the one you look for or reach the end. It returns the place (the index) or a sign for "not there": -1, because there is no place -1.
See it
The pointer on the shelf
static int find(List<Product> shelf, String barcode) { for (int i = 0; i < shelf.size(); i++) { if (shelf.get(i).barcode().equals(barcode)) { return i; } } return -1;}int rice = find(shelf, "3800200102690");System.out.println("3800200102690 -> " + rice + " " + shelf.get(rice).name());System.out.println("3800200100000 -> " + find(shelf, "3800200100000"));3800200102690 -> 3 Rice3800200100000 -> -1static int find(List<Product> shelf, String barcode) { for (int i = 0; i < shelf.size(); i++) { if (shelf.get(i).barcode().equals(barcode)) { return i; } } return -1;}
int rice = find(shelf, "3800200102690");int missing = find(shelf, "3800200100000");Step by step, in words
- The scanner reads the barcode of the rice: 3800200102690. find starts at the first box.
- It compares the barcode of every box with the one it looks for: flour, sugar, oil. No match.
- The fourth box is the rice. return 3: found after 4 comparisons.
- A barcode the warehouse does not have: 3800200100000. find starts from the beginning again.
- The pointer passes every box, and none matches.
- The boxes run out: return -1 after 6 comparisons. A missing product is the worst case: n comparisons for n boxes.
The rice is found after 4 comparisons. The missing barcode goes through all 6 boxes: that is the only way to say it is not there.
The barcodes are compared with equals, not with ==. For text, == asks whether it is the same object in memory, and the barcode from the scanner is a different object with the same digits.
Try it
Fill in
What does find return when the boxes run out?
static int find(List<Product> shelf, String barcode) {
for (int i = 0; i < shelf.size(); i++) {
if (shelf.get(i).barcode().equals(barcode)) {
return i;
}
}
return ;
}-1 is not a place in the list, so it cannot be mistaken for a found product. Whoever calls find checks for -1 before using the result.The idea
How many comparisons
The number of comparisons depends on where the product is:
- the best case: the first box, 1 comparison;
- the worst case: the last box or a product that is not there, n comparisons;
- the usual case: if every place is equally likely, about n/2 on average.
The worst and the usual case grow with n, so linear search is O(n). The program counts:
static int comparisons(List<Product> shelf, String barcode) { int count = 0; for (Product product : shelf) { count++; if (product.barcode().equals(barcode)) { return count; } } return count;}System.out.println("Flour: " + comparisons(shelf, "3800200101112"));System.out.println("Rice: " + comparisons(shelf, "3800200102690"));System.out.println("Missing: " + comparisons(shelf, "3800200100000"));Flour: 1Rice: 4Missing: 6Run the two extreme cases on a big warehouse. We count one comparison per box:
- The barcode is missing (worst case)
O(n) - The first product (best case)
O(1)
| Algorithm | n = 10 | n = 1,000 | n = 100,000 |
|---|---|---|---|
| The barcode is missing (worst case) | 10 | 1,000 | 100,000 |
| The first product (best case) | 1 | 1 | 1 |
The first box costs 1 comparison for every n: O(1). A missing product costs n: with 100 000 products that is 100 000 comparisons. The memory is O(1): one index.
Try it
Predict the output
The salt is the fifth box on the shelf. How many comparisons does the search make?
static int comparisons(List<Product> shelf, String barcode) { int count = 0; for (Product product : shelf) { count++; if (product.barcode().equals(barcode)) { return count; } } return count;}
System.out.println("Salt: " + comparisons(shelf, "3800200109958"));See it
The built-ins search the same way
Every language has a ready search in a list. Inside it is the same loop:
List<String> barcodes = List.of( "3800200101112", "3800200104403", "3800200107251", "3800200102690", "3800200109958", "3800200105837");System.out.println(barcodes.indexOf("3800200102690"));System.out.println(barcodes.indexOf("3800200100000"));System.out.println(barcodes.contains("3800200102690"));3-1trueindexOf returns the place or -1, and contains only "yes" or "no". Both compare with equals from the first element on: O(n). For a list of products by barcode Java has no ready method; you write the loop or use a stream (IntStream.range(...).filter(...).findFirst()), which also goes in order.One call looks like one operation, but inside there are up to n comparisons. The built-in function is faster than your loop by some constant factor, not by an order.
Try it
Find the bug
A colleague wrote the search like this. The flour is found, and the rice "is not there":
static int find(List<Product> shelf, String barcode) { for (int i = 0; i < shelf.size(); i++) { if (shelf.get(i).barcode().equals(barcode)) { return i; } else { return -1; } } return -1;}System.out.println(find(shelf, "3800200101112"));System.out.println(find(shelf, "3800200102690"));0-1Which line is the bug on?
Pick the line with the bug.
An else with a return inside the loop returns "not there" right after the first box that does not match. The loop never gets to the second one. The test with the flour passes, because it is first. "Not there" can only be said after the boxes run out: remove the else and keep the single "not there" after the loop.
The idea
When it is right and when it is not
Linear search is the right choice when:
- there are few products: ten or a hundred comparisons are not noticeable;
- you search once: every faster way first needs at least n steps to prepare;
- the data is unsorted or keeps changing, and there is nothing to maintain.
It is not when there are many searches. 2000 scans a day in 50 000 products are up to 100 000 000 comparisons. Then you prepare the data once:
- a table barcode → product (
HashMap): 50 000 steps once, and after that every search is O(1) on average; - a sorted list and binary search: sorting is O(n log n) once, and every search after it is O(log n), about 16 comparisons for 50 000.
Map<String, Product> byBarcode = new HashMap<>();for (Product product : shelf) { byBarcode.put(product.barcode(), product);}System.out.println(byBarcode.get("3800200102690").name());System.out.println(byBarcode.get("3800200100000"));RicenullThe table costs memory: another n places. It is the same trade of time for memory as the set in the lesson "Space complexity".
Exercise
All the boxes of one product
Solve the task in your editor.
Stocktaking. One product may have come with several deliveries and be in several places in the list. Write findAll(shelf, barcode), which returns all places of the barcode, not just the first one. The shelf is as above, plus more rice and more salt at the end (places 6 and 7).
Rice: 3, 6Salt: 4, 7More tasks
- The last place. Return the last place of the barcode. Where is it better to start?
- By name, ignoring case. Find the product by name ("rice" and "Rice" are the same). How many comparisons are there in the worst case?
- Products under the minimum. List all products with fewer than 10 items in stock. Can you stop before the end?
Test
Check yourself
Related lessons
Section titled "Related lessons"- Builds on: Time complexity: counting steps · Big-O: how the work grows · Best, average and worst case · Space complexity
- Continues in: Binary search · Binary search on the answer · Trie · Prefix sums
- Tasks on this topic: Searching