算法学习(六): 二分搜索

2018-06-09  本文已影响0人  squall1744

定义


从一个简单问题说起

给定一个排序并不存在重复元素的数组: [1,2,5,7,8,9,13], 查找8的位置

暴力解法: 遍历整个数组, 找到与给定值相同的元素, 返回下标, 时间复杂度O(n)

另一种解法:

  1. 我们可以先取数组中间位置的值, 看中间位置的值和目标值的大小, 假如中间位置的值大于目标值, 则说明目标值处于数组的中间位置的左半部分, 假如中间位置的值小于目标值, 则说明目标值处于数组中间位置的右半部分
  2. 这里我们假设数组中间位置的值大于目标值, 则说明目标值处于数组中间值的左半部分, 那么右半部分我们就不需要再去查找了
  3. 对于中间值的左半部分我们继续取中间位置的值, 像上面那样我们继续判断中间位置的值的大小, 大于目标值我们取左半部分, 小于目标值我们取右半部分
  4. 按照上面每次去一半的方法一直分下去, 直到数组中的某个位置值与目标值相等, 返回该位置

时间复杂度: 由于我们每次分割都会少掉一半, 所以时间复杂度为O(logn)

二分搜索

上面这种每次取一半搜索的方法就是二分搜索

二分搜索注意事项

function binarySearch(arr, target) {
  if (arr.length < 1) {
    return -1
  }
  let start = 0
  let end = arr.length - 1
  while (start + 1 < end) {
    let mid = Math.floor(start + (end - start) / 2)
    if (arr[mid] >= target) {
      end = mid
    }else if (arr[mid] < target) {
      start = mid
    }
  }
  
  if (arr[start] === target) {
    return start
  }
  if (arr[end] === target) {
    return end
  }
return -1
}

例题


搜索插入位置

给定一个排序数组和一个目标值, 如果在数组中找到了目标值则返回索引。
如果没有, 返回到它将会被按顺序插入的位置。
你可以假设在数组中无重复元素

case1:
输入: [1,3,5,6], 5 输出: 2
case2:
输入: [1,3,5,6], 2 输出: 1
case3:
输入: [1,3,5,6], 7 输出: 4
case4:
输入: [1,3,5,6], 0 输出: 0

分析:

function searchInsert(arr, target) {
  if (arr.length < 1) {
    return 0
  }
  
  let start = 0
  let end = arr.length - 1
  
  while (start + 1 < end) {
    let mid = Math.floor(start + (end - start) / 2)
    if (arr[mid] < target) {
      start = mid
    } else {
      end = mid
    }
  }
  if (arr[start] === target) {
    return start
  }
  if (arr[end] === target) {
    return end
  }
  if (target > arr[end]) {
    return end + 1
  }
  if (target < arr[start]) {
    return 0
  }
  return start + 1
}

搜索二维矩阵

编写一个高效的算法来搜索m*n矩阵中的一个目标值。
该矩阵具有以下特性:
每行中的整数从左向右排序。
每行的第一个数大于前一行的最后一个整数。

例如:
以下矩阵:
1   2   3   4
5   6   7   8
11 13 15 17
56 78 89 98
给定一个目标值3, 返回下标

分析:

function binarySearch(matrix, target) {
  if (matrix.length < 1 || matrix[0].length < 1) {
    return -1
  }
  
  let start = 0
  let end = matrix.length * matrix[0].length - 1

  while (start + 1 < end) {
    let mid = Math.floor(start + (end - start) / 2)
    if (matrix[Math.floor(mid / matrix[0].length)][mid % matrix[0].length] < target) {
      start = mid
    } else {
      end = mid
    }
  }
  if (matrix[Math.floor(start / matrix[0].length)][start % matrix[0].length] === target) {
    return [Math.floor(start / matrix[0].length), start % matrix[0].length]
  }
  if (matrix[Math.floor(end / matrix[0].length)][end % matrix[0].length] === target) {
    return [Math.floor(end / matrix[0].length), end % matrix[0].length]
  }
  return -1
}

求整数的平方根

给定一个整数, 返回它的平方根, 平方根不是整数的, 向下取整
例如:
输入: 25 输出: 5
输入: 0 输出: 0
输入: 9 输出: 3
输入: 7 输出: 2

分析:
这道题可以用二分搜索来做

function mySqrt(n) {
 if (n === 0) {
   return 0
 }else if (n < 0) {
   return 'error'
 }
 
 let start = 0
 let end = n
 while (start + 1 < end) {
   let mid = Math.floor(start + (end - start) / 2)
   if (mid * mid < n) {
     start = mid
   } else {
     end = mid
   }
 }
 if (end === n / end) {
   return end
 }
 return start
}

上一篇下一篇

猜你喜欢

热点阅读