Home / Computer Science / Class XII / Searching
Searching — CUET Computer Science hero
Class XII 💻 Computer Science Ch 6 of 20

Searching

CUET unit: Searching

📌 Snapshot

  • Why a billion sorted records need only about 30 steps: linear, binary and hash search explained with traces.
  • How the three searches differ in what they need (nothing, a sorted list, or a hash table) and how many comparisons they make.
  • Worked traces for linear search, binary search (found and not found) and a hash table, plus collisions and perfect hash functions.
  • The Python programs for all three searches, written out line by line so you can predict their output in a test.

📖 Detailed Notes

Written and reviewed by Rohit Singh, Computer Science & Commerce faculty at UniDrill (MCA, Osmania University · 6 years teaching). Based on NCERT Computer Science Class XII, Chapter 6.

How does a phone find one contact among a billion?

Open a dictionary and you do not start at page one. You flip to the middle, see that the word you want comes later, and throw away the first half in one move. A computer can do the same with data, and the trick is so strong that a sorted list of about a billion records can be searched in roughly 30 steps instead of a billion. This chapter is about three ways of searching a list, what each one demands from your data, and how much work each one does.

Searching means locating a particular element, called the key, in a collection. The result tells you whether the key is present and, if it is, where.

Technique What the list must look like Work in the worst case Idea in one line
Linear search Anything: sorted or unsorted $n$ comparisons Check every element in order
Binary search Must be sorted About $\log_2 n$ iterations Compare with the middle and discard half
Search by hashing Stored in a hash table built by a hash function 1 comparison if there is no collision Calculate where the key must be

Linear search: check every element in order

Linear search (also called sequential or serial search) compares the key with each element one by one, from the first towards the last. It stops with success at the first match, or with failure after the whole list has been checked. It needs no order at all, so it is the right tool for a small, unordered list.

The algorithm sets index = 0, repeats while index < n, compares numList[index] with the key, prints index + 1 as the position when they match, and otherwise increments the index. Notice the last step: the position reported to the user is index + 1, because people count from 1 while Python counts from 0.

Worked example: tracing linear search. Let numList = [8, -4, 7, 17, 0, 2, 19], so n = 7, and search for the key 17.

index index < n numList[index] == 17 ? Next index
0 0 < 7, yes 8 = 17? No 1
1 1 < 7, yes -4 = 17? No 2
2 2 < 7, yes 7 = 17? No 3
3 3 < 7, yes 17 = 17? Yes stop

The key is found after 4 comparisons at index 3, so the program reports position 4. The common slip is to report 3.

How much work linear search does depends only on where the key sits. The same seven numbers show all three cases.

Where the key is Example list (key 17) Comparisons Case
First element [17, 8, -4, 7, 0, 2, 19] 1 Best case
Inside the list [8, -4, 7, 17, 0, 2, 19] 4 Between the two extremes
Last element [8, -4, 7, 0, 2, 19, 17] 7 = $n$ Worst case
Not present at all any list without 17 7 = $n$ Worst case

Failing is as expensive as finding the last element, because only after looking at everything can you say "not here". The time grows in step with the size of the list, which is why linear search becomes slow for large lists.

Program: linear search as a function. The function returns the position (index plus one) or None.

Code
def linearSearch(list, key):
    for index in range(0, len(list)):
        if list[index] == key:
            return index + 1
    return None

Calling it on the list from the trace:

Code
list1 = [8, -4, 7, 17, 0, 2, 19]
position = linearSearch(list1, 17)
if position is None:
    print("Number 17 is not present in the list")
else:
    print("Number 17 is present at position", position)
Output
Number 17 is present at position 4

If the list holds duplicates, such as [42, -2, 32, 8, 17, 19, 42, 13, 8, 44], searching for 8 returns position 4. The loop returns as soon as it meets the first match, so later copies are never reached.

Pause and predict: In the list [7, -1, 11, 32, 17, 19, 23, 29, 31, 37, 43], how many comparisons does linear search make to find the key 43?

Check your answer: 11. The key 43 is the last of the 11 elements, so every element has to be compared. This is the worst case, $n$ comparisons.

Binary search: halve the problem every time

Binary search uses the order of a sorted list. You compare the key with the middle element and one of three things happens: it matches (done), the middle is greater than the key (the key can only be in the first half), or the middle is smaller than the key (the key can only be in the second half). Either way, half the list is gone after a single comparison, and you repeat on what is left. The list may be sorted in ascending or descending order, and text works too if it is in alphabetical order.

The reason it needs a sorted list is the sentence "the key can only be in the first half". That is true only if everything before the middle is smaller. In an unsorted list the sentence is false and you would throw away the very half that holds the key.

The algorithm keeps three markers, first = 0, last = n - 1 and mid = (first + last) // 2, and repeats while first <= last. Three moves are possible inside the loop.

Comparison Meaning Action
numList[mid] == key Found Report position mid + 1 and stop
numList[mid] > key Key lies before the middle last = mid - 1
numList[mid] < key Key lies after the middle first = mid + 1

When the loop ends because first > last, the search area is empty and the answer is "Search unsuccessful". The list itself never changes. Only the three index markers move.

The floor division // matters. For 15 elements, mid = (0 + 14) // 2 = 7. For an even count such as 10 elements, mid = (0 + 9) // 2 = 4, the left one of the two middle elements, so the two halves hold 4 and 5 elements.

Worked example: binary search, key found. Let numList = [2, 3, 5, 7, 10, 11, 12, 17, 19, 23, 29, 31, 37, 41, 43] (15 elements, indexes 0 to 14) and search for 23.

Iteration first last mid numList[mid] Decision
1 0 14 7 17 23 > 17, so first = 8
2 8 14 11 31 23 < 31, so last = 10
3 8 10 9 23 Match: position 10

Three iterations were enough, whereas linear search would have needed 10 comparisons for this key. If the key is exactly the middle element (17 here), binary search finishes in 1 iteration, its best case.

Worked example: the worst case. Search the same list for 2, the very first element.

Iteration first last mid numList[mid] Decision
1 0 14 7 17 2 < 17, so last = 6
2 0 6 3 7 2 < 7, so last = 2
3 0 2 1 3 2 < 3, so last = 0
4 0 0 0 2 Match: position 1

The number of elements still in play went 15, 7, 3, 1: halved each time. That is 4 iterations, the maximum for this list.

Worked example: key not present. Search the same list for 9.

Iteration first last mid numList[mid] Decision
1 0 14 7 17 9 < 17, so last = 6
2 0 6 3 7 9 > 7, so first = 4
3 4 6 5 11 9 < 11, so last = 4
4 4 4 4 10 9 < 10, so last = 3

Now first = 4 and last = 3, so first <= last is false and the loop ends: the search is unsuccessful after 4 iterations. A common wrong step is to stop at the third row because "9 lies between 7 and 11". The algorithm cannot know that until the search area is empty.

Program: binary search as a function. This version returns the index mid, or -1 when the key is absent, so the caller adds 1 to print a position.

Code
def binarySearch(list, key):
    first = 0
    last = len(list) - 1
    while first <= last:
        mid = (first + last) // 2
        if list[mid] == key:
            return mid
        elif key > list[mid]:
            first = mid + 1
        elif key < list[mid]:
            last = mid - 1
    return -1
Code
list1 = [1, 3, 4, 5]
pos = binarySearch(list1, 4)
if pos != -1:
    print(4, "is found at position", pos + 1)
else:
    print(4, "is not found in the list")
Output
4 is found at position 3

Notice the two return conventions. Linear search returns index + 1 (a position) and None for failure. Binary search returns the raw mid (an index) and -1 for failure. Questions on this are really questions on reading the return statement.

Why it is so fast. After each iteration the area is halved, so the number of iterations in the worst case is $\lfloor \log_2 n \rfloor + 1$.

Elements $n$ Linear search (worst) Binary search (worst)
15 15 4
1,023 1,023 10
1,024 1,024 11
1,000,000 1,000,000 20
$2^{30}$ (1,073,741,824) 1,073,741,824 31

If the person you want sits at the exact middle of a sorted database of $2^{30}$ records, binary search finds them in 1 iteration, while linear search must compare about 537 million records first. That gap is why binary search ideas sit inside databases, routing tables in routers and compression codes, and why a dictionary or telephone directory is searched this way.

Beyond the textbook. For the exam: binary search needs a sorted list, and the sorting happens before the search. In reality: sorting a list costs more than one linear search over it, so binary search pays off when you search the same data many times, such as a contact book or a database index. For a single search in an unsorted list, plain linear search is the cheaper choice.

Pause and predict: A list of 10 sorted numbers is [2, 5, 8, 12, 16, 23, 38, 56, 72, 91]. How many iterations does binary search need for the key 23, and what does the code return?

Check your answer: 3 iterations, returns index 5. First mid = (0 + 9) // 2 = 4 holds 16, and 23 > 16 so first = 5. Next mid = (5 + 9) // 2 = 7 holds 56, and 23 < 56 so last = 6. Then mid = (5 + 6) // 2 = 5 holds 23, a match. The function returns the index 5, and the printed position is 6.

If you already knew which position a key must sit at, one comparison would settle the question. Hashing makes that possible. A hash function takes an element and produces an index; the elements are stored in a hash table at those indexes. To search, you apply the same function to the key, look at that one slot, and compare.

The simplest hash function is the remainder method, and it only works on numbers.

$$h(\text{element}) = \text{element} \bmod \text{size of the hash table}$$

In Python this is element % size. The remainder is the hash value, which is also the index in the table. Each slot of the table can hold only one item, and the table may be larger than the list.

Worked example: building a hash table. Store the list [34, 16, 2, 93, 80, 77, 51] in a hash table of 10 positions using h(element) = element % 10.

Element Calculation Hash value (index)
34 34 % 10 4
16 16 % 10 6
2 2 % 10 2
93 93 % 10 3
80 80 % 10 0
77 77 % 10 7
51 51 % 10 1

Placing each element at its index gives the table:

Index 0 1 2 3 4 5 6 7 8 9
Value 80 51 2 93 34 None 16 77 None None

To search for 16, compute 16 % 10 = 6, look at index 6, and find 16: present, with a single comparison. To search for 46, compute 46 % 10 = 6, find 16 there, and 16 is not 46: absent, again with a single comparison. The time is the same whether the list has seven items or seven million, because the cost is one calculation plus one comparison.

Program: building a hash table and searching it.

Code
def hashFind(key, hashTable):
    if hashTable[key % 10] == key:
        return (key % 10) + 1
    else:
        return None
hashTable = [None, None, None, None, None, None, None, None, None, None]
L = [34, 16, 2, 93, 80, 77, 51]
for i in range(0, len(L)):
    hashTable[L[i] % 10] = L[i]
print(hashTable)
print(hashFind(16, hashTable))
print(hashFind(46, hashTable))
Output
[80, 51, 2, 93, 34, None, 16, 77, None, None]
7
None

The function returns (key % 10) + 1, the position, so 16 at index 6 is reported as position 7.

Collision. Hashing works perfectly only when every element maps to its own slot. Take the list [34, 16, 2, 26, 80] with h(element) = element % 10. Both 16 and 26 give the hash value 6, and one slot cannot hold two items. This clash is called a collision. Finding another slot for the element that lost the clash is called collision resolution. Several methods exist, but they are beyond this chapter, so the exam only asks you to recognise a collision and to know the term.

A hash function that maps every element of the list to a unique index is a perfect hash function, and with a perfect hash function collisions never occur. Real lists rarely allow that, so a perfect hash function is the ideal and collision resolution is the everyday reality.

Worked example: a bad table size. Use h(element) = element % 11 for [44, 121, 55, 33, 110, 77, 22, 66]. Every number is a multiple of 11, so every remainder is 0.

Element 44 121 55 33 110 77 22 66
Element % 11 0 0 0 0 0 0 0 0

All eight elements fight for index 0, so seven collisions occur. The lesson is that the speed of hashing depends on how well the function spreads the data. A function that is cheap to compute but sends everything to one slot defeats the purpose.

Other hash functions exist apart from the remainder method: integer division, shift folding, boundary folding, mid-square, extraction and radix transformation. You only need to know their names. The time to compute a hash value does not depend on the number of items in the list, but it must be small enough that hashing still beats the other methods.

Beyond the textbook. For the exam: hashing is "one comparison", provided there is no collision. In reality: the dictionary and the set that you use in Python are built on hash tables, which is why checking key in my_dict stays fast even for huge dictionaries. They handle collisions behind the scenes, so the programmer never sees them.

Situation Best choice Reason
Small list, no order Linear search No sorting needed and the work is tiny
Large list that is already sorted Binary search About $\log_2 n$ iterations
Many searches on the same large data, exact match on a number Hash table One comparison per search
Unsorted list, one single search Linear search Sorting first costs more than searching
Need to find the position in an alphabetical list of words Binary search Text sorted a to z can be halved like numbers

Exam traps

The trap Why it's tempting The truth
"Binary search works on any list." It is the "fast" search, so it seems universal The list must be sorted, ascending or descending
"For 10 elements the first mid is index 5." Ten halved is five mid = (first + last) // 2 = (0 + 9) // 2 = 4
"Linear search returns the index." Python lists use indexes The program returns index + 1, the position
"Binary search's worst case for 15 elements is 3." $\log_2 15$ is about 3.9 It is 4 iterations, as $\lfloor \log_2 n \rfloor + 1$
"Hashing always needs exactly one comparison." The chapter says "one step" Only if there is no collision at that slot
"A collision means the key is not found." Both sound like failures A collision happens while storing: two elements share one hash value
"The hash table must be as small as the list." Saving space seems sensible The table can be larger than the list
"Linear search finds the last duplicate." The loop goes to the end It stops at the first match

60-second revision

Topic Lock this in
Key The element being searched for
Linear search Any list · best 1 · worst $n$ · returns index + 1 or None
Binary search Sorted list only · mid = (first + last) // 2 · numList[mid] > key gives last = mid - 1 · else first = mid + 1
Binary stop rule Stop when found, or when first > last
Binary worst case $\lfloor \log_2 n \rfloor + 1$ iterations (15 elements: 4)
Hash function element % size of table gives the index
Hash search One comparison, if no collision
Collision Two elements, same hash value
Collision resolution Finding another slot for the clashing element
Perfect hash function Every element gets a unique index, so no collision

Where this chapter connects

  • Previous: Sorting arranges a list in order, which is exactly the condition binary search needs.
  • Next: Understanding Data moves from searching a list to collecting, storing and summarising data.
  • Same ideas elsewhere: Stack and Queue are other list based data structures built on the same Python list operations.

🎯 Practice MCQs

First 3 questions free · create a free account to unlock the rest — answers & explanations included, no payment needed

Q1. Which statement about linear search is correct?

▸ Show answer & explanation

Answer: B

Linear search checks elements one after another and works on sorted or unsorted lists. Option A is a requirement of binary search, option C describes binary search and option D describes hashing.

Q2. A list has n elements and the key is not present. How many comparisons does linear search make?

▸ Show answer & explanation

Answer: C

The whole list must be checked before the search can declare failure, so n comparisons are made. This is the worst case, the same as when the key is the last element.

Q3. A function f(lst, key) loops over every index i of lst from 0 to len(lst) - 1 and returns i + 1 as soon as lst[i] equals key. If the loop finishes without a match, it returns None. What is printed by print(f([10, 20, 30, 40], 30))?

▸ Show answer & explanation

Answer: B

The key 30 is at index 2, and the function returns index + 1, which is 3. Option A is the index of 20, option C is the value rather than the position, and option D would be returned only if 30 were absent.

🔒 12 more practice MCQs

Create a free account to unlock every MCQ in this chapter — answers and explanations included. No payment needed.

Already registered? Just log in and they'll all appear here.

📊 Previous-Year Questions

Practise with real CUET Computer Science previous-year papers — every question solved, with the correct answer and a step-by-step explanation.

View solved CUET PYQ papers →

Ready to drill Computer Science?

Unlock all MCQs, chapter tests, mocks & PYQs for ₹199/year.

Get UniDrill Pro