前言
今天接着学习牛客网的二分查找算法,不断刷类似的题目,让我们能够更快的掌握这种方法。
二分查找法的主要思路:
1.从数组首尾开始,每次取中点值。
2.如果中间值等于目标即找到了,可返回下标,如果中点值大于目标,说明中点以后的都大于目标,因此目标在中点左半区间,如果中点值小于目标,则相反。
3.根据比较进入对应的区间,直到区间左右端相遇,意味着没有找到。
1.BM17 二分查找-I
描述
请实现无重复数字的升序数组的二分查找
给定一个 元素升序的、无重复数字的整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标(下标从 0 开始),否则返回 -1
进阶:时间复杂度 O(\log n)O(logn) ,空间复杂度 O(1)O(1)
实例1
输入:
[-1,0,3,4,6,10,13,14],13
返回值:
6
说明:
13 出现在nums中并且下标为 6
实例2
输入:
[],3
返回值:
-1
说明:
nums为空,返回-1
我的答案
import java.util.*;
public class Solution {
public int search (int[] nums, int target) {
// 定义左右两个指针
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = (left + right)/2;
if(nums[mid] == target){
return mid;
}
//如果target比nums[mid]大,说明在mid右边区域,需要移到右半部分
else if(nums[mid] < target){
left = mid + 1;
}
//如果target比nums[mid]小,说明在mid左边区域,需要移到左半部分
else{
right = mid - 1;
}
}
//如果循环结束之后,都没有出现,说明数组为空或者这个数不存在
return -1;
}
}
2.BM18 二维数组中的查找
描述
在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]
]
给定 target = 7,返回 true。
给定 target = 3,返回 false。
进阶:空间复杂度 O(1)O(1) ,时间复杂度 O(n+m)O(n+m)
示例1
输入:
7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
返回值:
true
说明:
存在7,返回true
示例2
输入:
1,[[2]]
返回值:
false
实例3
输入:
3,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
返回值:
false
说明:
不存在3,返回false
我的答案:
public class Solution {
public boolean Find(int target, int [][] array) {
//没啥好说的,和上一题基本思路一样,就是在提交之后加了对空数组的判断
//然后因为是二维数组,多加了一次遍历,从上一个数组到下一个数组
if (array.length == 0 || array[0].length == 0){
return false;
}
for(int i = 0; i < array.length; i++){
int left = 0;
int right = array[i].length - 1;
while(left <= right){
int mid = (left + right)/2;
if(target == array[i][mid]){
return true;
}
else if(target > array[i][mid]){
left = mid + 1;
}
else{
right = mid - 1;
}
}
}
return false;
}
}
3.BM19 寻找峰值
描述
给定一个长度为n的数组nums,请你找到峰值并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个所在位置即可。
1.峰值元素是指其值严格大于左右相邻值的元素。严格大于即不能有等于
2.假设 nums[-1] = nums[n] = -\infty−∞
3.对于所有有效的 i 都有 nums[i] != nums[i + 1]
4.你可以使用O(logN)的时间复杂度实现此问题吗?
如输入[2,4,1,2,7,8,4]时,会形成两个山峰,一个是索引为1,峰值为4的山峰,另一个是索引为5,峰值为8的山峰,如下图所示:
示例1
输入:
[2,4,1,2,7,8,4]
返回值:
1
说明:
4和8都是峰值元素,返回4的索引1或者8的索引5都可以
示例2
输入:
[1,2,3,1]
返回值:
2
说明:
3 是峰值元素,返回其索引 2
我的答案:
import java.util.*;
public class Solution {
public int findPeakElement (int[] nums) {
int left = 0;
int right = nums.length - 1;
while(left < right){
int mid = (left + right)/2;
//如果mid比right大,说明峰值在左边
if(nums[mid] > nums[right]){
right = mid;
}
//如果mid比right大,说明峰值在右边
else{
left = mid + 1;
}
}
return right;
}
}
4.BM21 旋转数组的最小数字
描述
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
数据范围:1 \le n \le 100001≤n≤10000,数组中任意元素的值: 0 \le val \le 100000≤val≤10000
要求:空间复杂度:O(1)O(1) ,时间复杂度:O(logn)O(logn)
示例1:
输入:[3,4,5,1,2]
返回值:1
示例2:
输入:[3,100,200,3]
返回值:3
我的答案:
import java.util.*;
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
//其实找最小数字就是找旋转点,因为在旋转之前数组是有序的
//定义两个指针
int left = 0;
int right = array.length - 1;
while (left < right){
int mid = (left + right)/2;
//说明旋转点在右边
if(array[mid] > array[right]){
left = mid + 1;
}
//缩小范围判断,因为这是个旋转数组
else if(array[mid] == array[right]){
right--;
}
//说明旋转点在左边
else{
right = mid;
}
}
return array[right];
}
}
总结:
其实二分查找法整体来说还是一种比较简单的算法,只要在使用过程中注意区间的开闭即可。