1.普通版二分查找
普通版的只要注意到区间范围就好,我使用的是左闭右闭方法,注意以下几点就可以。
第一种【】左闭右闭
- right = nums.length - 1;
- left < = right
- right = mid -1;
- left = mid +1
另一种选择:
[ ) 左闭右开
- right = nums.length;
- left < right
- right = mid ;
- left = mid +1
牛客链接
二分查找-I_牛客题霸_牛客网 (nowcoder.com)
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型一维数组
* @param target int整型
* @return int整型
*/
public int search (int[] nums, int target) {
int result = 0;
int left = 0 ;
int right = nums.length - 1;
while(left <= right){
int middle = (left + right)/2;
if(nums[middle] > target){
right = middle - 1 ;
}else if(nums[middle] < target){
left = middle + 1;
}else{
return middle;
}
}
// 未找到目标值
return -1;
}
}
2.排序数组中查找第一个元素和最后一个元素
力扣链接如下:
34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
这个题的关键点在于怎么查找出左边界和右边界,先上一个暴力解法的版本
暴力解法
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = 0;
int right = 0;
int temp = 0;
for(int i = 0; i < nums.length; i++){
if(nums[i] == target){
left = i;
temp = 1;
break;
}
}
for(int j = left; j <nums.length ;j++){
if(nums[j] != target){
right = j-1;
break;
}
right = j;
}
if(temp == 1){
return new int[]{left,right};
}else{
return new int[]{-1,-1};
}
}
}
二分法查找
使用二分查找的方法的话,就是先找到左边界的第一个目标元素,在找到右边界的目标元素,使用两次二分查找即可。
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = searchLeftBorder(nums,target);
int right = searchRightBorder(nums,target);
// 这里要对边界条件进行一个判断
if(left <= right && right <= nums.length ){
return new int[]{left,right};
}
return new int[]{-1,-1};
}
// 获取左边界
// 判断左边界就是找到第一个等于target的位置
public int searchLeftBorder(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int first = -1;
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] == target){
first = mid;
// 因为要获取的是左边界,现在第一个查找出来的也不知道是不是最左的
// 所有把二分查找的右边界移到现在查找出来的值的左边
right = mid -1;
}else if(nums[mid] > target){
right = mid -1;
}else if(nums[mid] < target){
left = mid + 1;
}
}
return first;
}
// 获取右边界
// 就是找到最后一个等于target的位置
public int searchRightBorder(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int last = -1;
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] == target){
last = mid;
// 因为要获取的是右边界,现在第一个查找出来的也不知道是不是最右的
// 所有把二分查找的左边界移到现在查找出来的值的右边
left = mid + 1;
}else if(nums[mid] > target){
right = mid -1;
}else if(nums[mid] < target){
left = mid + 1;
}
}
return last;
}
}
3.第一个错误版本
力扣链接:
这个题目的关键在于理解题意
// 如果一个版本是正确的,那这个版本之前的都是正确的
// 如果一个版本是错误的,那这个版本之后的都是错误的
// 通过二分查找限定范围
// 起始左边为1,右边为n
while循环不能写成left<= right ,因为如果n =1就成死循环了
// 注意 isBadVersion为false时,代表是正确版本
注意到这几点后就和普通的二分查找是一样的了
/* The isBadVersion API is defined in the parent class VersionControl.
boolean isBadVersion(int version); */
// 如果一个版本是正确的,那这个版本之前的都是正确的
// 如果一个版本是错误的,那这个版本之后的都是错误的
// 通过二分查找限定范围
// 起始左边为1,右边为n
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left = 1;
int right = n;
// 注意这里不能写 = 号
// 因为如果n=1 的话就进入死循环了
while(left < right){
int mid = left + (right - left) / 2;
// 注意 isBadVersion为false时,代表是正确版本
// 刚开始直接把题意给理解错误了,导致写反了
if(isBadVersion(mid)){
// mid是错误版本
right = mid -1;
}else {
// mid 是正确版本
left = mid + 1;
}
}
return left;
}
}
4.寻找两个正序数组的中位数
力扣链接:
暴力解法
首先来一个暴力的解法,这里的核心思想就是,先将两个有序数组进行合并,然后根据合并后的数组是奇数还是偶数进行判断。
代码如下:
class Solution {
// 暴力解法,将两个数组进行合并,然后根据奇数还是偶数返回中位数
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int length = nums1.length + nums2.length;
int nums[] = new int[length];
nums = mergeArrays(nums1,nums2);
// 奇数
if(nums.length % 2 != 0){
return nums[(nums.length - 1)/2];
}else{
double result = (nums[nums.length/2] + nums[nums.length/2 - 1])/2.0;
return result;
}
}
// 两个数组合并的代码
public int[] mergeArrays(int[]nums1,int[] nums2){
int length = nums1.length + nums2.length;
int j = 0;
int i = 0;
int k = 0;
int[] nums = new int[length];
while(i < nums1.length && j < nums2.length){
if(nums1[i] < nums2[j]){
nums[k++] = nums1[i++];
}else{
nums[k++] = nums2[j++];
}
}
// 如果提前把nums1数组遍历完了,就把nums2剩余的全部添加进去
while(j < nums2.length){
nums[i+j] = nums2[j++];
}
while(i < nums1.length){
nums[i+j] = nums1[i++];
}
return nums;
}
}
二分法解法
大家肯定想着用暴力的解法都可以了,为什么还要二分呢,是因为这道题目的要求是时间复杂度为:O(log(m+n))
看到时间复杂度为log,通常就需要用二分法了。
在分割线右边找
在分割线左边找,向 左边移动
下面这种数组越界的情况,要解决的话,就得让长度最小的那个数组充当第一个数组,
要满足的条件:
完整代码:
这个题思路还是难,算了还是用暴力法吧,清楚明了