二分查找

55 阅读2分钟

二分查找的前提:数据有序

二分查找的条件分支:

  1. nums[mid]==target
  2. nums[mid]<target
  3. nums[mid]>target

二分查找的搜索区间表示形式:

  • 左闭右开

    区间为空的条件是left==right

  • 左闭右闭

    区间为空的条件是left>right

二分查找结束的条件:查找区间为空,或者找到元素

二分查找有的时候我会写出死循环来,仔细思考了一下,必须每次收缩边界的时候,都排除掉mid。

习题:

  1. 704.二分查找(简单)
  2. 在排序数组中查找元素的第一个和最后一个位置(中等)

习题解答:

第二题和第一题不同的地方在于结束条件,第二题的结束条件只能是查找区间为空,找到元素后还要继续进行查找。

第二题查找结束后,还必须对查找的最后一个元素进行判断,如果等于target就返回index,不等于就返回-1。由于有三个分支,我们只需要考虑最后的元素进入nums[mid]==target分支的情况,其余的分支必定不相等。那么我们假设它进了这个分支,并对这个最后位置的元素再重新判断一次,如果相等就找到了,如果不相等就没找到。

第一题答案:

左闭右闭搜索区间的写法:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let left = 0;
    let right = nums.length-1;
    while(left<=right){
        const mid = left + Math.floor((right-left)/2);
        if(nums[mid]==target){
            return mid;
        }else if(nums[mid]>target){
            right = mid-1;
        }else if(nums[mid]<target){
            left = mid+1;
        }
    }
    return -1;
};

左闭右开搜索区间的写法:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let left = 0;
    let right = nums.length;
    while(left<right){
        const mid = left + Math.floor((right-left)/2);
        if(nums[mid]==target){
            return mid;
        }else if(nums[mid]>target){
            right = mid;
        }else if(nums[mid]<target){
            left = mid+1;
        }
    }
    return -1;
};

第二题答案:

左闭右开搜索区间的写法:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var searchRange = function(nums, target) {
    return [findLeft(nums, target), findRight(nums, target)]
};

var findLeft = function(nums, target){
    let left = 0;
    let right = nums.length;
    while(left<right){
        let mid = left + Math.floor((right-left)/2);
        if(nums[mid]==target){
            right = mid;
        }else if(nums[mid]<target){
            left = mid+1;
        }else if(nums[mid]>target){
            right = mid;
        }
    }
    if(nums[left]==target){
        return left
    }
    return -1;
}

var findRight = function(nums, target){
    let left = 0;
    let right = nums.length;
    while(left<right){
        let mid = left + Math.floor((right-left)/2);
        console.log(left, mid, right)
        if(nums[mid]==target){
            left = mid+1;
        }else if(nums[mid]<target){
            left = mid+1;
        }else if(nums[mid]>target){
            right = mid;
        }
    }
    if(nums[right-1]==target){
        return right-1;
    }
    return -1;
}

左闭右闭搜索区间的写法:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var searchRange = function(nums, target) {
    return [fundLeft(nums, target), fundRight(nums, target)]
};

var fundLeft = function(nums, target){
    let left = 0;
    let right = nums.length - 1;
    while(left<=right){
        let mid = left + Math.floor((right-left)/2);
        if(nums[mid]==target){
            right = mid-1;
        }else if(nums[mid]<target){
            left = mid+1;
        }else if(nums[mid]>target){
            right = mid-1;
        }
    }
    if(nums[left]==target){
        return left;
    }
    return -1;
}

var fundRight = function(nums, target){
    let left = 0;
    let right = nums.length - 1;
    while(left<=right){
        let mid = left + Math.floor((right-left)/2);
        if(nums[mid]==target){
            left = mid+1;
        }else if(nums[mid]<target){
            left = mid+1;
        }else if(nums[mid]>target){
            right = mid-1;
        }
    }
    if(nums[right]==target){
        return right;
    }
    return -1;
}