给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
1 2 3 输入:nums = [-1,0,3,5,9,12],target = 9 输出: 4 解释: 9 出现在nums 中并且下标为 4
示例 2:
1 2 3 输入:nums = [-1,0,3,5,9,12],target = 2 输出: -1 解释: 2 不存在nums 中因此返回 -1
提示:
你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。
💡 思路:有序数组 数组中无重复元素 ⇒ 二分查找 当数组中有重复元素,使用二分查找法返回的元素下标可能不是唯一的
写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。
写法一(左闭右闭):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 impl Solution { pub fn search (nums: Vec <i32 >, target: i32 ) -> i32 { let mut left = 0i32 ; let mut right = nums.len () as i32 -1 ; while left <= right { let middle = left + (right - left) / 2 ; match nums[middle as usize ].cmp (&target) { std::cmp::Ordering::Less => left = middle + 1 , std::cmp::Ordering::Greater => right = middle -1 , std::cmp::Ordering::Equal => return middle, } } -1 } }
在 Rust 中,**0usize - 1** 会导致 panic(程序崩溃) ,因为 usize 是无符号整数(不能为负数),而减法下溢(underflow)在 debug 模式下会触发 panic。
💡 在Release 模式下( -release 编译) 由于溢出检查被禁用 代码let right = 0usize - 1;的结果会按照无符号整数的回绕(wrapping)规则计算:0usize - 1 = usize::MAX(例如 64 位系统上是 18446744073709551615) 这会导致后续的数组访问越界(nums[middle] 会 panic),因为索引远大于数组长度。
写法二(左闭右开):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 impl Solution { pub fn search (nums: Vec <i32 >, target: i32 ) -> i32 { let mut left = 0 ; let mut right = nums.len (); while left < right { let middle = left + (right - left) / 2 ; match nums[middle].cmp (&target) { std::cmp::Ordering::Less => left = middle + 1 , std::cmp::Ordering::Greater => right = middle, std::cmp::Ordering::Equal => return middle as i32 , } } -1 } }
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
1 2 输入: nums = [1,3,5,6], target = 5 输出: 2
示例 2:
1 2 输入: nums = [1,3,5,6], target = 2 输出: 1
示例 3:
1 2 输入: nums = [1,3,5,6], target = 7 输出: 4
提示:
1 <= nums.length <= 104
104 <= nums[i] <= 104
nums 为 无重复元素 的 升序 排列数组
104 <= target <= 104
💡 思路:nums 为 无重复元素 的 升序 排列数组,有序数组 + 无重复数据 尝试使用二分法
代码:
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 use std::cmp::Ordering;impl Solution { pub fn search_insert (nums: Vec <i32 >, target: i32 ) -> i32 { let mut left = 0 ; let mut right = nums.len (); let mut middle ; while left < right { middle = left + (right - left) / 2 ; match nums[middle].cmp (&target) { Ordering::Less => left = middle + 1 , Ordering::Greater => right = middle, Ordering::Equal => return middle as i32 , } } left as i32 } } impl Solution { pub fn search_insert (nums: Vec <i32 >, target: i32 ) -> i32 { nums.partition_point (|&x| x < target) as _ } }
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
1 2 输入:nums = [5,7,7,8,8,10], target = 8 输出:[3,4]
示例 2:
1 2 输入:nums = [5,7,7,8,8,10], target = 6 输出:[-1,-1]
示例 3:
1 2 输入:nums = [], target = 0 输出:[-1,-1]
提示:
0 <= nums.length <= 105
109 <= nums[i] <= 109
nums 是一个非递减数组
109 <= target <= 109
💡 思路: 非递减 → 有序 但是 数组中有重复数据 → 二分查找变式
代码:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 use std::cmp::Ordering;impl Solution { pub fn search_range (nums: Vec <i32 >, target: i32 ) -> Vec <i32 > { let start = Self ::binary_search_fast (&nums,target); let end = Self ::binary_search_last (&nums,target); if start <= end { return [start,end].to_vec (); }else { return [-1 ,-1 ].to_vec (); } } fn binary_search_fast (nums: &[i32 ], target: i32 ) -> i32 { let mut left = 0 ; let mut right = nums.len (); let mut middle ; while left < right { middle = left + (right - left) / 2 ; match nums[middle].cmp (&target) { Ordering::Less => left = middle + 1 , Ordering::Greater | Ordering::Equal => right = middle, } } left as i32 } fn binary_search_last (nums: &[i32 ], target: i32 ) -> i32 { let mut left = 0 ; let mut right = nums.len (); let mut middle ; while left < right { middle = left + (right - left) / 2 ; match nums[middle].cmp (&target) { Ordering::Less | Ordering::Equal => left = middle + 1 , Ordering::Greater => right = middle, } } left as i32 - 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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 use std::cmp::Ordering;impl Solution { pub fn search_range (nums: Vec <i32 >, target: i32 ) -> Vec <i32 > { if nums.is_empty () { return vec! [-1 , -1 ]; } let first_pos = Self ::find_boundary (&nums, target, true ); if first_pos == -1 { return vec! [-1 , -1 ]; } let last_pos = Self ::find_boundary (&nums, target, false ); vec! [first_pos, last_pos] } fn find_boundary (nums: &[i32 ], target: i32 , find_first: bool ) -> i32 { let mut left = 0 ; let mut right = nums.len (); let mut result = -1 ; while left < right { let mid = left + (right - left) / 2 ; match nums[mid].cmp (&target) { Ordering::Less => left = mid + 1 , Ordering::Greater => right = mid, Ordering::Equal => { result = mid as i32 ; if find_first { right = mid; } else { left = mid + 1 ; } } } } result } }
利用标准库:
1 2 3 4 5 6 7 8 pub fn search_range (nums: Vec <i32 >, target: i32 ) -> Vec <i32 > { let left = nums.partition_point (|&x| x < target); if left == nums.len () || nums[left] != target { return vec! [-1 , -1 ]; } let right = nums.partition_point (|&x| x <= target) - 1 ; vec! [left as i32 , right as i32 ] }
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意: 不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1:
示例 2:
1 2 3 输入:x = 8 输出:2 解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
提示:
💡 思路: 对于非负整数 m 来说,由于 m 越小越满足 m²≤x,m 越大越不满足 m²≤x,有单调性,可以二分答案。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 use std::cmp::Ordering;impl Solution { pub fn my_sqrt (x: i32 ) -> i32 { let mut left = 0 ; let mut right = x.min (46340 ) + 1 ; while left + 1 < right{ let middle = left + (right - left) / 2 ; match middle.cmp (&(x / middle)) { Ordering::Less | Ordering::Equal => left = middle, Ordering::Greater => right = middle, } } 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 use std::cmp::Ordering;impl Solution { pub fn my_sqrt (x: i32 ) -> i32 { if x < 2 { return x; } let mut left = 1 ; let mut right = (x / 2 ).min (46340 ) + 1 ; while left < right { let mid = left + (right - left) / 2 ; match mid.cmp (&(x / mid)) { Ordering::Equal => return mid, Ordering::Less => left = mid + 1 , Ordering::Greater => right = mid, } } left - 1 } }
给你一个正整数 num 。如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。
不能使用任何内置的库函数,如 sqrt 。
示例 1:
1 2 3 输入:num = 16 输出:true 解释:返回 true ,因为 4 * 4 = 16 且 4 是一个整数。
示例 2:
1 2 3 输入:num = 14 输出:false 解释:返回 false ,因为 3.742 * 3.742 = 14 但 3.742 不是一个整数。
提示:
💡 思路: 正整数序列查找x²=num,以1~(x / 2).min(46340) + 1作为区间,判断x²==num返回true否者返回false
代码:
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 use std::cmp::Ordering;impl Solution { pub fn is_perfect_square (num: i32 ) -> bool { if num < 1 { return false ; } if num == 1 { return true ; } let mut left = 1 ; let mut right = (num / 2 ).min (46340 ) + 1 ; while left < right { let middle = left + (right - left) / 2 ; match (middle*middle).cmp (&num) { Ordering::Equal => return true , Ordering::Less => left = middle + 1 , Ordering::Greater => right = middle, } } false } }