Array Binary Search
Description Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value.
If target is not found in the array, return [-1, -1].
You must write an algorithm with O(log n) runtime complexity.
Example 1:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
Example 2:
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
Example 3:
Input: nums = [], target = 0
Output: [-1,-1]
Constraints:
0 <= nums.length <= 105 -109 <= nums[i] <= 109 nums is a non-decreasing array. -109 <= target <= 109 Solutions Solution 1: Binary Search Thinking
The first idea is a linear scan that records the first and last hits. Correct, but \(n \le 10^5\) and the problem requires \(O(\log n)\) . Expanding outward from one match is still \(O(n)\) in the worst case.
The array is non-decreasing, so the two endpoints are separate binary-search questions; we need not find both in one pass.
The left endpoint is the first index \(\ge target\) ; the right endpoint is the first index \(\ge target+1\) , minus one. Two lower-bound queries suffice; if they land on the same index, \(target\) is absent.
We can perform two binary searches to find the left boundary and the right boundary.
The time complexity is \(O(\log n)\) , where \(n\) is the length of the array \(\textit{nums}\) . The space complexity is \(O(1)\) .
Python3 Java C++ Go TypeScript Rust JavaScript C# PHP Kotlin
class Solution :
def searchRange ( self , nums : List [ int ], target : int ) -> List [ int ]:
l = bisect_left ( nums , target )
r = bisect_left ( nums , target + 1 )
return [ - 1 , - 1 ] if l == r else [ l , r - 1 ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 class Solution {
public int [] searchRange ( int [] nums , int target ) {
int l = search ( nums , target );
int r = search ( nums , target + 1 );
return l == r ? new int [] { - 1 , - 1 } : new int [] { l , r - 1 };
}
private int search ( int [] nums , int x ) {
int left = 0 , right = nums . length ;
while ( left < right ) {
int mid = ( left + right ) >>> 1 ;
if ( nums [ mid ] >= x ) {
right = mid ;
} else {
left = mid + 1 ;
}
}
return left ;
}
}
class Solution {
public :
vector < int > searchRange ( vector < int >& nums , int target ) {
int l = lower_bound ( nums . begin (), nums . end (), target ) - nums . begin ();
int r = lower_bound ( nums . begin (), nums . end (), target + 1 ) - nums . begin ();
if ( l == r ) {
return { -1 , -1 };
}
return { l , r - 1 };
}
};
func searchRange ( nums [] int , target int ) [] int {
l := sort . SearchInts ( nums , target )
r := sort . SearchInts ( nums , target + 1 )
if l == r {
return [] int { - 1 , - 1 }
}
return [] int { l , r - 1 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 function searchRange ( nums : number [], target : number ) : number [] {
const search = ( x : number ) : number => {
let [ left , right ] = [ 0 , nums . length ];
while ( left < right ) {
const mid = ( left + right ) >> 1 ;
if ( nums [ mid ] >= x ) {
right = mid ;
} else {
left = mid + 1 ;
}
}
return left ;
};
const l = search ( target );
const r = search ( target + 1 );
return l === r ? [ - 1 , - 1 ] : [ l , r - 1 ];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 impl Solution {
pub fn search_range ( nums : Vec < i32 > , target : i32 ) -> Vec < i32 > {
let n = nums . len ();
let search = | x | {
let mut left = 0 ;
let mut right = n ;
while left < right {
let mid = left + ( right - left ) / 2 ;
if nums [ mid ] < x {
left = mid + 1 ;
} else {
right = mid ;
}
}
left
};
let l = search ( target );
let r = search ( target + 1 );
if l == r {
return vec! [ - 1 , - 1 ];
}
vec! [ l as i32 , ( r - 1 ) as i32 ]
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 /**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var searchRange = function ( nums , target ) {
function search ( x ) {
let left = 0 ,
right = nums . length ;
while ( left < right ) {
const mid = ( left + right ) >> 1 ;
if ( nums [ mid ] >= x ) {
right = mid ;
} else {
left = mid + 1 ;
}
}
return left ;
}
const l = search ( target );
const r = search ( target + 1 );
return l == r ? [ - 1 , - 1 ] : [ l , r - 1 ];
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 public class Solution {
public int [] SearchRange ( int [] nums , int target ) {
int l = Search ( nums , target );
int r = Search ( nums , target + 1 );
return l == r ? new int [] { - 1 , - 1 } : new int [] { l , r - 1 };
}
private int Search ( int [] nums , int x ) {
int left = 0 , right = nums . Length ;
while ( left < right ) {
int mid = ( left + right ) >>> 1 ;
if ( nums [ mid ] >= x ) {
right = mid ;
} else {
left = mid + 1 ;
}
}
return left ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 class Solution {
/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer[]
*/
function searchRange ( $nums , $target ) {
$search = function ( $x ) use ( $nums ) {
$left = 0 ;
$right = count ( $nums );
while ( $left < $right ) {
$mid = intdiv ( $left + $right , 2 );
if ( $nums [ $mid ] >= $x ) {
$right = $mid ;
} else {
$left = $mid + 1 ;
}
}
return $left ;
};
$l = $search ( $target );
$r = $search ( $target + 1 );
return $l === $r ? [ - 1 , - 1 ] : [ $l , $r - 1 ];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 class Solution {
fun searchRange ( nums : IntArray , target : Int ): IntArray {
val left = this . search ( nums , target )
val right = this . search ( nums , target + 1 )
return if ( left == right ) intArrayOf ( - 1 , - 1 ) else intArrayOf ( left , right - 1 )
}
private fun search ( nums : IntArray , target : Int ): Int {
var left = 0
var right = nums . size
while ( left < right ) {
val middle = ( left + right ) / 2
if ( nums [ middle ] < target ) {
left = middle + 1
} else {
right = middle
}
}
return left
}
}