二分查找算法
将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果x<a[n/2],则只要在数组a的左半部分继续搜索x,如果x>a[n/2],则只要在数组a的右半部搜索x
0-1模型
可以抽象为待查找元素是否满足查找条件,满足标记为1不满足标记为0
二分查找要保证待查找元素一定在待查找区间中
LeetCode肝题
-
- x 的平方根
// 在0和x+1之间查找第一个平方大于x的值,返回该值的下标-1
var mySqrt = function(x) {
let head = 0, tail = x + 1, mid
while(head < tail) {
mid = parseInt((head + tail) / 2)
if (mid * mid > x) tail = mid
else if (mid * mid == x) return mid
else head = mid + 1
}
return head - 1
};
-
- 搜索插入位置
// 在nums数组中查找第一个大于等于target的位置
var searchInsert = function(nums, target) {
let head = 0, tail = nums.length, mid
while(head < tail) {
mid = (head + tail) >> 1
if (nums[mid] >= target) tail = mid
else head = mid + 1
}
return head
};
-
- 在排序数组中查找元素的第一个和最后一个位置
// 查两次,第一次查大于等于target的第一个位置,第二次查大于等于target+1的位置
var binary_search_01 = function(nums, target) {
let head = 0, tail = nums.length, mid, ans = []
while(head < tail) {
mid = (head + tail) >> 1
if (nums[mid] >= target) tail = mid
else head = mid + 1
}
return head
}
var searchRange = function(nums, target) {
let ans = [-1, -1]
ans[0] = binary_search_01(nums, target)
if (nums[ans[0]] != target) return [-1, -1]
ans[1] = binary_search_01(nums, target + 1) - 1
return ans
};
-
- 将 x 减到 0 的最小操作数
// 正向和反向求nums的两个前缀和数组,最小操作数其实就是两个前缀和数组相加之和等于x的下标之和
function binary_search(nums, target) {
let head = 0, tail = nums.length - 1, mid
while(head <= tail) {
mid = (head + tail) >> 1
if (nums[mid] == target) return mid
else if (nums[mid] < target) head = mid + 1
else tail = mid - 1
}
return -1
}
var minOperations = function(nums, x) {
let suml = Array(nums.length + 1).fill(0), sumr = Array(nums.length + 1).fill(0), ans = -1
for(let i = 0; i < nums.length; i++) suml[i+1] = nums[i] + suml[i]
for(let i = nums.length - 1; i >= 0; i--) sumr[nums.length - i] = sumr[nums.length - i - 1] + nums[i]
for(let i = 0; i < suml.length; i++) {
let j = binary_search(sumr, x - suml[i])
if (j == -1) continue
if (i + j > nums.length) continue
if (ans == -1 || ans > (i + j)) ans = i + j
}
return ans
};
-
- 供暖器
// 将供暖器数组排序,遍历房屋数组,二分查找找到大于等于该房屋的位置j,取j和j-1位置的供暖器与房屋的差值较小的最大值
var binary_search_01 = function(nums, target) {
let head = 0, tail = nums.length - 1, mid
while(head < tail) {
mid = (head + tail) >> 1
if (nums[mid] >= target) tail = mid
else head = mid + 1
}
return head
}
var findRadius = function(houses, heaters) {
heaters = heaters.sort((a, b) => a - b)
let ans = 0
for(let i = 0; i < houses.length; i++) {
let j = binary_search_01(heaters, houses[i])
let a = Math.abs(heaters[j] - houses[i])
let b = j > 0 ? houses[i] - heaters[j - 1] : a + 1
ans = Math.max(ans, Math.min(a, b))
}
return ans
};
-
- 搜索旋转排序数组 II
// 首先设置并调整头尾指针,让查找范围第一个元素大于最后一个元素
// 分为两种查找情况,第一种:mid位置在后面的递增区间,如果target大于中间值小于tail值,调整l到mid+1,否则调整r到mid-1
// 第二种:mid位置在前面的递增区间,如果target小于中间值大于head值,调整r到mid-1,否则调整l到mid+1
var search = function(nums, target) {
if (nums[0] == target || nums[nums.length - 1] == target) return true
let l = 0, r = nums.length - 1, mid, head, tail = r
while(nums[l] == nums[r] && l < r) ++l
head = l
while(l <= r) {
mid = (l + r) >> 1
if (nums[mid] == target) return true
if (nums[mid] <= nums[tail]) {
if (target > nums[mid] && target <= nums[tail]) l = mid + 1
else r = mid - 1
} else {
if (target < nums[mid] && target >= nums[head]) r = mid - 1
else l = mid + 1
}
}
return false
};
-
- 寻找两个正序数组的中位数
// 有点变态
// 在两个有序数组中寻找第k个元素
var findK = function(nums1, nums2, i, j, k) {
if (i == nums1.length) return nums2[j + k - 1]
if (j == nums2.length) return nums1[i + k - 1]
if (k == 1) return nums1[i] < nums2[j] ? nums1[i] : nums2[j]
let a = Math.min(k >> 1, nums1.length - i)
let b = Math.min(k - a, nums2.length - j)
a = k - b
if (nums1[i + a - 1] <= nums2[j + b - 1]) {
return findK(nums1, nums2, i + a, j, k - a)
}
return findK(nums1, nums2, i, j + b, k - b)
}
var findMedianSortedArrays = function(nums1, nums2) {
let n = nums1.length, m = nums2.length, mid = (n + m + 1) >> 1
let a = findK(nums1, nums2, 0, 0, mid)
if ((n + m) % 2 == 1) return a
let b = findK(nums1, nums2, 0, 0, mid + 1)
return (a + b) / 2
};