二分查找法
例子:猜数字
猜100以内的一个数字,先猜50,大了就锁定范围1-49,小了就锁定范围51-100。以此类推,每次猜中间的数来锁定范围,直到猜出数字。
例子:数组搜索元素
给定一个有序数组,搜索数组重是否存在某个元素。如果使用遍历,时间复杂度是O(N)。如果使用二分查找发,时间复杂度是O( l o g 2 N log_2N log2N),因为每次排除一半的元素。
特点
查找的元素必须有序。
二分查找法相关leetcode
No.704 二分查找
思路:先判断数组是否为空,是则返回-1。然后定义左指针和右指针分别指向数组的第一个元素和最后一个元素,以及中间指针。因为当左指针大于右指针时说明已经遍历完了整个数组,所以当左指针小于右指针时,进入循环,让mid指向左右指针的中间位置,如果mid所指元素大于target,说明target在左半边,让右指针指向mid-1;如果mid所指元素小于target,说明target在右半边,让左指针指向mid+1,如果mid所指元素正好等于targe,直接返回mid。最后循环结束,即数组遍历结束后如果还没有找到target,返回-1。
class Solution {
public int search(int[] nums, int target) {
if(nums == null || nums.length == 0)
return -1;
int left = 0;
int right = nums.length - 1;
int mid = 0;
while(left <= right) {
mid = (left + right) / 2;
if(nums[mid] < target) {
left = mid + 1;
} else if(nums[mid] > target) {
right = mid - 1;
} else {
return mid;
}
}
return -1;
}
}
No.162 寻找峰值
思路:因为题目中的扩展说了时间复杂度为O(logN),所以应该想到二分查找。
如果元素是单调递增的,也就是元素的下一个元素比当前元素大,那么峰值应该在元素的右边。我们应该往右边移动。
如果元素是单调递减的,也就是元素的下一个元素比当前元素小,那么峰值应该在元素的左边。我们应该往左边移动。
以上图为例,将左指针和右指针指向1和4,中间元素是3,3的下一个元素比3大,那么峰值在3的右边,将左指针移动到3的下一个元素5。接下来中间元素是6,6的下一个元素比6小,峰值在6或着6的左边,将右指针指向6。接下来中间元素是5,5的下一个元素比5大,峰值在5的右边,将左指针指向5的下一个元素6,这里左右指针相遇,返回左指针即可。
class Solution {
public int findPeakElement(int[] nums) {
int left = 0;
int right = nums.length - 1;
int mid = 0;
while(left < right) {
mid = (left + right) / 2;
if(nums[mid] < nums[mid + 1]) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
}
No.35 搜索插入位置
思路:定义左右指针指向数组的第一个位置和最后一个位置,以及中间指针。当左指针小于右指针时进入循环,因为左指针等于右指针时不能确定插入位置在前在后。循环内中间指针指向左右指针的中间位置,判断当前元素是否等于target,是则返回当前索引;如果当前元素小于target,左指针移动到mid+1,因为要插入的位置一定在当前元素之后,但一定不是当前元素的位置;如果当前元素大于target,将右指针移动到mid,因为要插入的位置有可能是mid的位置或者mid之前的位置。最后判断左指针指向的元素是否大于等于target,是则表明target应该插在左指针前,也就是顶替了左指针所指元素的位置,否则表明target应该插在左指针之后,也就是左指针+1的位置上。
class Solution {
public int searchInsert(int[] nums, int target) {
//常规法
// if(nums == null || nums.length == 0)
// return 0;
// for(int i = 0; i < nums.length; i++) {
// if(nums[i] >= target) {
// return i;
// }
// }
// return nums.length;
//二分法
if(nums == null || nums.length == 0)
return 0;
int left = 0;
int right = nums.length - 1;
int mid = 0;
while(left < right) {
mid = (left + right) / 2;
if(nums[mid] == target) {
return mid;
} else if(nums[mid] < target) {
left = mid + 1;
} else {
right = mid;
}
}
return nums[left] >= target ? left : left + 1;
}
}
No.74 搜索二位矩阵
难点:将二维矩阵转为一位矩阵,只需要将二维矩阵的索引转为一位矩阵的索引。
一维矩阵的索引=二位矩阵的x*总列数+y
二维矩阵的索引=(一维矩阵的索引/总列数,一维矩阵的索引%总列数)
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if(matrix == null || matrix.length == 0)
return false;
int row = matrix.length;
int col = matrix[0].length;
int left = 0;
int right = row * col - 1;
int mid = 0;
while(left <= right) {
mid = (left + right) / 2;
if(matrix[mid / col][mid % col] == target) {
return true;
} else if(matrix[mid / col][mid % col] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
}