CodeGround
DashboardProblemsInterviewsAssessmentsCourses
CodeGround
DashboardProblemsInterviewsAssessmentsCourses
The Technical Interview Course

0 of 170 lessons complete

  • Finding where a property flips
  • Searching something that is not a list
  • Searching the answer instead of the input
  • Discarding a half without full sortedness
  • Working out which half is trustworthy
  • The bisect module, and search inside a design
  • Searching for a split
  • Retrieval: spotting a hidden binary search

Binary Search

40 / 170

Binary Search/Lesson 2 of 8·About 33 min

Searching something that is not a list

Any structure you can index in order can be binary searched, including a grid.

Key idea

The loop does not care what it is searching

The template from the last lesson never touches a list. It manipulates numbers and calls a property. So it can search anything you can address by an integer position, provided the property stays monotone.

A grid whose rows are sorted and where each row starts above the previous row's end is, read row by row, one long sorted sequence. It just is not stored that way.

Why it works

Position to coordinates

Number the cells from 0 in reading order. For a grid with cols columns, position p sits at row p // cols and column p % cols.

Integer division counts how many complete rows fit before you, and the remainder is how far along the current row you are. This is the same arithmetic as the block index from Unit 1, used for the opposite purpose: there it collapsed coordinates into a region, here it expands a position into coordinates.

1grid = [2    [1, 3, 5],3    [7, 9, 11],4]5cols = len(grid[0])6 7for position in range(len(grid) * cols):8    print(position, grid[position // cols][position % cols])

With that mapping, searching the grid is the ordinary boundary loop with one substitution.

1def grid_contains(grid, target):2    if not grid or not grid[0]:3        return False4    rows, cols = len(grid), len(grid[0])5    low, high = 0, rows * cols6 7    while low < high:8        mid = low + (high - low) // 29        value = grid[mid // cols][mid % cols]10        if value >= target:11            high = mid12        else:13            low = mid + 114 15    return low < rows * cols and grid[low // cols][low % cols] == target16 17print(grid_contains([[1, 3, 5], [7, 9, 11]], 9))18print(grid_contains([[1, 3, 5], [7, 9, 11]], 4))

Edge cases

Guard the shape first

An empty grid, or a grid whose rows are empty, breaks len(grid[0]) before any searching happens. Check the shape before computing cols.

Also confirm the problem's guarantee. This works because each row starts above the previous row's last value. If rows are individually sorted but rows do not chain that way, the flattened sequence is not sorted and this approach is simply wrong; that variant needs a staircase walk from a corner instead.

Tip

The two-stage alternative

You can also binary search for the right row, then binary search within it. That is two O(log) searches instead of one, so the complexity is the same, and it needs no index arithmetic.

Either is a fine answer. The flattened version is shorter; the two-stage version is easier to explain out loud. Say which you are doing and why.

Quick check· 5 min

Where does position 7 sit?

A grid has 3 columns. Using reading order numbered from 0, which cell is position 7?

Problem· 18 min

Search A 2d Matrix

Read the constraints first and let them tell you what complexity is expected. Derive the approach, implement it, run the tests, and submit when it passes.

PreviousFinding where a property flips
NextSearching the answer instead of the input

0 of 3 parts complete