Start learning free

LESSON 92 OF 414 · CONDITIONAL STATEMENTS - CHALLENGES

Interval Intersection in Rust: O(1) Boolean Check

Practice Read the full explanation

Given two closed intervals [A1, B1] and [A2, B2], check and return if the two intervals intersect each other.

A closed interval [a, b] (with a <= b) denotes the set of real numbers x with a <= x <= b.

Two closed intervals are said to intersect if they have common numbers, i.e. that appear in both intervals.

Example 1:

Input: A1 = 1, B1 = 6, A2 = 3, B2 = 8
Output: true

Example 2:

Input: A1 = 1, B1 = 4, A2 = 4, B2 = 6
Output: true

Example 3:

Input: A1 = 1, B1 = 8, A2 = 4, B2 = 6
Output: true

Example 4:

Input: A1 = 4, B1 = 6, A2 = 1, B2 = 3
Output: false

Note:

Your algorithm should run in O(1) time and use O(1) extra space.


Hints:

Instead of thinking about when the intervals intersect each other, it's easier to think about when they do not intersect each other.

The intervals do not intersect each other when one of them is "to the left" of the other one i.e. B1 < A2 or B2 < A1.

We can write an if statement checking if B1 < A2 or B2 < A1. If so, we return false. Otherwise, we return true.

You can implement this using no else statements. You can even implement it in one line using only one return.
STEP 1 OF 1
    Ctrl + Enter to check

    RUST · CONDITIONAL STATEMENTS - CHALLENGES

    Interval Intersection in Rust, explained

    Problem overview

    The function intersect receives two closed intervals, [a1, b1] and [a2, b2], as four i32 values, and returns a bool: do the intervals share at least one number? A closed interval includes both of its ends, so [1, 4] and [4, 6] share the number 4.

    Examples: [1, 6] and [3, 8] overlap on 3 to 6, so the answer is true. [1, 4] and [4, 6] only touch at 4, which still counts, so true. [1, 8] contains [4, 6], so true. [4, 6] and [1, 3] are apart, so false. The lesson asks for O(1) time and space, so a fixed number of comparisons and no loops.

    Approach

    Listing every way two intervals can overlap is tedious: partial overlap from either side, one inside the other, touching ends. Listing the ways they can miss each other is much shorter. They miss exactly when one lies completely to the left of the other:

    • the first ends before the second starts: b1 < a2, or
    • the second ends before the first starts: b2 < a1.

    If neither holds, the intervals share a number, so the answer is the negation of "one of those holds". The comparisons are strict because touching ends, as in [1, 3] and [3, 5], share the point 3 and must count as intersecting.

    Step by step

    1. Write the two "apart" conditions: b1 < a2 and b2 < a1.
    2. Join them with ||. The intervals are apart if either condition holds, so you need "or".
    3. Put the combined condition in parentheses and negate it with !. Without the parentheses, ! would apply to b1 < a2 alone.
    4. Return the result in a single return statement. It is false when the intervals are apart and true otherwise.

    The solution in Rust

    impl Solution {
        fn intersect(a1: i32, b1: i32, a2: i32, b2: i32) -> bool {
            return !(b1 < a2 || b2 < a1);
        }
    }
    

    Rust details worth knowing:

    • Comparisons produce a bool. b1 < a2 is an expression whose value is true or false, so you can return it, combine it and negate it directly without an if.
    • ||, && and !. Rust uses these three symbols for or, and and not, not the words. || and && short-circuit: if b1 < a2 is already true, the second comparison isn't evaluated.
    • The final expression. The function can also drop the return and the semicolon, leaving !(b1 < a2 || b2 < a1) as its last expression.
    • Equivalent form. By De Morgan's laws, the same test reads a2 <= b1 && a1 <= b2: each interval must start no later than the other ends. Both forms always agree.

    Complexity

    Time: O(1). The function performs two comparisons and a negation, no matter how large the numbers are.

    Space: O(1). No data is stored besides the four parameters.

    Edge cases and common mistakes

    • Touching ends. Using <= in the "apart" conditions would report [1, 4] and [4, 6] as not intersecting. Closed intervals include their ends, so the comparisons must be strict.
    • One inside the other. [1, 8] and [4, 6] give false for both "apart" conditions, so the result is true. No special case is needed.
    • Order of the two intervals. Swapping the intervals swaps the two conditions, so the result is the same either way. [4, 6] with [1, 3] is apart through b2 < a1.
    • Missing parentheses. Writing !b1 < a2 || b2 < a1 compiles but means something else: ! binds tighter than <, so it applies to the integer b1. On an integer, ! flips every bit instead of negating a condition, so the comparison uses a changed number.
    • Single point intervals. [3, 3] and [3, 3] intersect, and [3, 3] with [4, 4] do not.

    Testing

    • a1 = 1, b1 = 6, a2 = 3, b2 = 8 gives true.
    • a1 = 1, b1 = 4, a2 = 4, b2 = 6 gives true.
    • a1 = 1, b1 = 8, a2 = 4, b2 = 6 gives true.
    • a1 = 4, b1 = 6, a2 = 1, b2 = 3 gives false.
    • a1 = 3, b1 = 3, a2 = 4, b2 = 4 gives false.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free