Skip to main content

Command Palette

Search for a command to run...

Binary Search in Clojure

Updated
2 min read
Binary Search in Clojure

I decided to implement linear search before going after binary search. Now, I’m ready!

With linear search, we required the elements be sorted. We didn’t require the elements be bounded. In binary search, the inputs must be bounded because we maintain two indexes or pointers that start at the beginning and end of the search space. Because we move one index half-way closer to the other each iteration, the time complexity is O(log n).

Solution

Loop recur was a nice combination for linear search. So, we’ll use it again here.

(defn binary-search
  [x xs]
  (loop [low 0
         high (dec (count xs))]
    (when (<= low high)
      (let [mid (quot (+ low high) 2)
            mid-val (xs mid)]
        (cond
          (= x mid-val) mid
          (< x mid-val) (recur low (dec mid))  ;; x in left half, scoot high index
          :else         (recur (inc mid) high) ;; x in right half, scoot low index
          )))))

Challenges

Two Are Better Than One

One of the hardest parts for me in figuring this out was that I forgot two indexes are necessary. I tried using a single index and it never converged.

Integer Division

In addition, my mid formula was wrong!

(let [low 1
      high 10]
  [(/ (+ low high) 2)      ;; 11/2
   (quot (+ low high) 2)]) ;; 5

Sometimes, / returns a clojure.lang.Ratio while quot always returns a java.lang.Long. I’ve never used quot before. In C, / truncates toward 0 when both inputs are ints, which is what we need.

Tests

(are [x xs i] (= (binary-search x xs) i)
  1 [] nil
  1 [1] 0
  1 [1 3] 0
  -1 [1 3 5 7 9] nil
  1 [1 3 5 7 9] 0
  3 [1 3 5 7 9] 1
  5 [1 3 5 7 9] 2
  7 [1 3 5 7 9] 3
  9 [1 3 5 7 9] 4
  10 [1 3 5 7 9] nil
  25 (vec (range 101)) 25
  75 (vec (range 101)) 75)