Problem overview
The function intersect receives the ends of two closed intervals, [a1, b1] and [a2, b2], and returns true when they share at least one number. "Closed" means the end points belong to the interval, so [1, 4] contains 4.
The examples show the cases:
[1, 6]and[3, 8]overlap on 3 to 6, so the answer istrue.[1, 4]and[4, 6]only touch, at the number 4. Because 4 is in both, the answer istrue.[1, 8]and[4, 6]: the second sits entirely inside the first, so the answer istrue.[4, 6]and[1, 3]are apart, so the answer isfalse.
The algorithm must run in O(1) time and use O(1) extra space, so no loop over the numbers in the intervals is allowed.
Approach: describe the misses, then negate
There are many ways two intervals can overlap: partly from the left, partly from the right, one inside the other, identical, touching at a point. Listing them all is error-prone. There are only two ways they can fail to overlap:
- The first interval ends before the second begins:
b1 < a2. - The second interval ends before the first begins:
b2 < a1.
If neither is true, the intervals must share a number. So the answer is the negation of "one of them is entirely to the left of the other".
The comparisons are strict (<, not <=), because intervals that touch at a single point do intersect. With b1 = 4 and a2 = 4, the test b1 < a2 is false, so the intervals count as overlapping.
Step by step
- Write the two miss conditions:
b1 < a2andb2 < a1. - Join them with
||. Either one alone is enough for the intervals to be apart, so "or" is the right connective. - Wrap the pair in parentheses and negate it with
!. The parentheses make the!apply to the whole group instead of just the first comparison. - Return the result directly. The expression is already a
bool, so noifis needed.
The solution in Go
func intersect(a1, b1, a2, b2 int) bool {
return !(b1 < a2 || b2 < a1)
}
Checking it against the examples: for 1, 6, 3, 8, 6 < 3 is false and 8 < 1 is false, so the group is false and the negation is true. For 4, 6, 1, 3, 6 < 1 is false but 3 < 4 is true, so the group is true and the function returns false.
Some Go details:
- Grouped parameters.
a1, b1, a2, b2 intdeclares all four parameters with typeint. It is shorthand for repeating the type on each one. - Returning a comparison. A comparison or a logical expression is a value of type
boolin Go, so it can be returned directly. Writingif cond { return true } else { return false }is longer and says nothing more. - Short-circuiting. If
b1 < a2is true, Go skips the second comparison. It makes no difference here, but it is how||always behaves. - The parentheses matter. Without them,
!b1 < a2 || ...would try to negate the integerb1, which does not compile.
Complexity
The function does at most two comparisons and one negation, so time is O(1). It stores nothing, so extra space is O(1). The size of the intervals doesn't matter, because only their end points are compared.
Edge cases and common mistakes
- Touching intervals.
intersect(1, 3, 3, 5)returnstrue. Using<=in the miss conditions would wrongly returnfalse. - Single-point intervals.
intersect(5, 5, 5, 5)returnstrue, because both intervals hold exactly the number 5.intersect(5, 5, 6, 6)returnsfalse. - Gaps by one.
intersect(1, 3, 4, 6)returnsfalse. For integers there is no number between 3 and 4, and the intervals share none. - Either order of arguments. The test is symmetric: swapping the two intervals swaps the two comparisons, and the result is the same.
- Using
&&for the misses. Requiring bothb1 < a2andb2 < a1can never succeed for valid intervals, so the function would always returntrue. - Forgetting the parentheses.
!b1 < a2 || b2 < a1does not compile, because!applies only tob1, which is an integer.
Testing
intersect(1, 6, 3, 8)givestrue.intersect(1, 4, 4, 6)givestrue.intersect(1, 8, 4, 6)givestrue.intersect(4, 6, 1, 3)givesfalse.intersect(1, 3, 5, 7)givesfalse.