二分查找

155 阅读1分钟

二分查找

leetcode-cn.com/problems/bi…

  • 给定一个n个元素有序的(升序)整型数组 nums 和一个目标值 target,写一个函数检索 nums中的 target, 如果目标值存在返回下标, 否则返回-1。
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    var len = nums.length;
    var left = 0, right = len - 1;
    if(len === 1){
        return target === nums[0]?0 : -1;
    }
    while(left <= right){
        var mid = Math.floor((left + right) / 2); //向下求整
        if(target < nums[mid]){ // 目标值在左侧
            right = mid - 1
        }else if(target > nums[mid]){ // 目标值在右侧
            left = mid + 1
        }else{ // 目标值在中间
            return mid;
        }
    }
    return -1;
};

-如 : 在[10,11,22,33,44,55,66]中找目标值10;

10的数组下标是0; 66的数组下标是6;

最小值中间值最大值说明
036数组下标0和6的中间值为(0+6)/2=3,下标3对应的值是33,目标值10<33;则新的最大值下标为3-1=2
012数组下标0和2的中间值为(0+2)/2=1,下标1对应得值是11,目标值10<11;则新的最大值下标为1-1=0
000数组下标0和0的中间值为(0+0)/2=0,下标0对应得值是10,目标值10==10;顾结果为10对应得下标0

寻找旋转排序数组中的最小值

leetcode-cn.com/problems/fi…

/**
 * @param {number[]} nums
 * @return {number}
 */
var findMin = function(nums) {
    var len = nums.length
    if(len === 1){
        return  nums[0]
    }
    var left = 0, right = len - 1;
    while(left <= right){
        var mid = Math.floor((left + right)/2);
        if(nums[mid] <= nums[right]){ //中间值 小于 最右侧值,则最小值等于中间值或者在左侧
            if(nums[mid] > nums[mid-1]){
                right = mid - 1;
            }else{ //最小值等于中间值的情况
                return nums[mid]
            }
        }else{ //中间值 大于最右侧值,则最小值在右侧
            left = mid + 1
        }
    }
    return nums[left]
};

搜索旋转排序数组

leetcode-cn.com/problems/se…

  • 整数数组nums按升序排序,数组中的值互不相等。
  • 在传递给函数之前,nums在预先未知的某个小标k(0<=k<=nums.length)上进行了旋转,使数组变为[nums[k], nums[k+1],..., nums[n-1], nums[0],nums[1],...,nums[k-1]]。例如,[0,1,2,4,5,6,7]在下标3处经旋转后可能变成[4,5,6,7,0,1,2]。
  • 给你旋转后的数组nums和一个整数target,如果nums中存在这个目标值target,则返回它的下标,否则返回-1。
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    var len = nums.length
    if(len === 1){
        return nums[0] === target?0: -1;
    }
    var left = 0, right = len -1;
    while(left <= right){
        var mid = Math.floor((left + right) / 2)
        if(target === nums[mid]){
            return mid;
        }
        //通过画曲线,分三段对比
        // 中间数小于最右数
        if(nums[mid] <= nums[right]){  
            if(target > nums[right]){
                right = mid - 1 
            }else if(target < nums[mid]){
                right = mid - 1
            }else{
                left = mid + 1
            }
        }else{
            if(target > nums[mid]){
                left = mid + 1
            }else if(target < nums[left]){
               left = mid + 1
            }else{
               right = mid - 1
            }
        }
       
    }
    return -1;
    
};



搜索旋转排序数组 II

leetcode-cn.com/problems/se…

  • 整数数组nums按升序排序,数组中的值不必互不相同
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {boolean}
 */
var search = function(nums, target) {
    var len = nums.length
    if(len == 1 && target == nums[0]){
        return true
    }
    var left = 0, right = len - 1
    while(left <= right){
        var mid = Math.floor((left + right) / 2)
        if(target === nums[mid]){
               return true
        }
        if(nums[left] === nums[mid] && nums[mid] === nums[right] ){ //相等情况的特殊处理
            left ++
            right --
        }else if(nums[mid] <= nums[right]){ //右边是升序
          if (target > nums[mid] && target <= nums[right]){
               left = mid + 1
           }else{
               right = mid  - 1
           }
        }else { //左边是升序
            if(target >= nums[left] && target < nums[mid]){
                right = mid - 1
            }else{
                left = mid + 1
            }
        }
    }
    return false
}

有序数组中的单一元素

leetcode-cn.com/problems/si… 给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNonDuplicate = function(nums) {
    var len = nums.length
    var left = 0, right = len - 1
    while(left <= right){
        var mid = Math.floor((left + right) / 2)
        //判断中间位置是奇数还是偶数
        //中间位置是偶数,前面就有偶数个整数
        //中间位置是奇数,前面就有奇数个整数

        var double = false
        if(mid%2 === 0){
            double = true
        } 
        if(nums[mid] === nums[mid + 1]){ 
            if(double){//中间位置为偶数,左边有偶数个整数,顾单数在右侧
                left = mid + 2
            }else{//中间位置为奇数,左边有奇数个整数,顾单数在左侧
                right = mid - 1
            }
            
        }else if (nums[mid] === nums[mid - 1]){
            if(double){ //中间位置为偶数,左边有偶数个整数,mid-1占了一个,剩余奇数个整数,则单数在左侧
                right = mid - 2
            }else{ //中间位置为奇数,左边有奇数个整数,mid-1占了一个,剩余偶数个整数,则单数在右侧
                left = mid + 1
            }
        }else{
            return nums[mid]
        }
    }
};

寻找峰值

leetcode-cn.com/problems/fi…

  • 峰值元素是指其值大于左右相邻值的元素
  • 给你一个输入数组nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。
/**
 * @param {number[]} nums
 * @return {number}
 */
var findPeakElement = function(nums) {
    var len = nums.length
    var left = 0, right = len - 1
    while(left < right){
        var mid = Math.floor((left + right)/2)
        if(nums[mid] > nums[mid + 1]){ //
            right = mid
        }else{
            left = mid + 1
        }
    }
     return left
};

var nums = [1,2,1,3,5,6,4]

  1. left=0 right=6 mid=3 nums[3]=3 nums[4]=5 nums[3]<nums[4] 则left=4
  2. left=4 right=6 mid=5 nums[5]=6 nums[6]=4 nums[5]>nums[6] 则right=5
  3. left=4 right=5 mid=4 nums[4]=5 nums[5]=6 nums[4]<nums[5] 则left=5
  4. left=5 right=5 则while循环终端