|
| 1 | +### 二分搜索 |
| 2 | + |
| 3 | +#### 二分搜索模板 |
| 4 | + |
| 5 | +给一个**有序数组**和目标值,找第一次/最后一次/任何一次出现的索引,如果没有出现返回-1 |
| 6 | + |
| 7 | +模板四点要素 |
| 8 | + |
| 9 | +- 1、初始化:start=0、end=len-1 |
| 10 | +- 2、循环条件:start <= end |
| 11 | +- 3、比较中点和目标值:A[mid] ==、 <、> target |
| 12 | +- 4、判断最后两个元素是否符合:A[start]、A[end] ? target |
| 13 | + |
| 14 | +时间复杂度 O(logn),使用场景一般是有序数组的查找 |
| 15 | + |
| 16 | +##### 704.二分查找 [binary-search](https://leetcode-cn.com/problems/binary-search/) |
| 17 | + |
| 18 | +```js |
| 19 | +// 二分搜索最常用模板 |
| 20 | +var search = function(nums, target) { |
| 21 | + let left = 0, mid = 0, right = nums.length-1; |
| 22 | + while(left + 1 < right){ |
| 23 | + mid = left + ((right-left) >> 2); |
| 24 | + if(nums[mid] === target){ |
| 25 | + return mid; |
| 26 | + } |
| 27 | + if(nums[mid] > target){ |
| 28 | + right = mid - 1; |
| 29 | + } else { |
| 30 | + left = mid + 1; |
| 31 | + } |
| 32 | + } |
| 33 | + // 最后剩下两个元素,手动判断 |
| 34 | + if (nums[left] === target) { |
| 35 | + return left; |
| 36 | + } |
| 37 | + if (nums[right] === target) { |
| 38 | + return right; |
| 39 | + } |
| 40 | + return -1; |
| 41 | +}; |
| 42 | +``` |
| 43 | + |
| 44 | +大部分二分查找类的题目都可以用这个模板,然后做一点特殊逻辑即可。 |
| 45 | + |
| 46 | +**模板 #1** (left <= right) |
| 47 | + |
| 48 | +二分查找的最基础和最基本的形式。 |
| 49 | +查找条件可以在不与元素的两侧进行比较的情况下确定(或使用它周围的特定元素)。 |
| 50 | +不需要后处理,因为每一步中,你都在检查是否找到了元素。如果到达末尾,则知道未找到该元素。 |
| 51 | + |
| 52 | +**模板 #2** (left < right) |
| 53 | + |
| 54 | +一种实现二分查找的高级方法。 |
| 55 | +查找条件需要访问元素的直接右邻居。 |
| 56 | +使用元素的右邻居来确定是否满足条件,并决定是向左还是向右。 |
| 57 | +保证查找空间在每一步中至少有 2 个元素。 |
| 58 | +需要进行后处理。 当你剩下 1 个元素时,循环 / 递归结束。 需要评估剩余元素是否符合条件。 |
| 59 | + |
| 60 | +**模板 #3** (left + 1 < right) |
| 61 | + |
| 62 | +实现二分查找的另一种方法。 |
| 63 | +搜索条件需要访问元素的直接左右邻居。 |
| 64 | +使用元素的邻居来确定它是向右还是向左。 |
| 65 | +保证查找空间在每个步骤中至少有 3 个元素。 |
| 66 | +需要进行后处理。 当剩下 2 个元素时,循环 / 递归结束。 需要评估其余元素是否符合条件。 |
| 67 | + |
| 68 | +如果是最简单的二分搜索,不需要找第一个、最后一个位置、或者是没有重复元素,可以使用**模板#1**,代码更简洁: |
| 69 | + |
| 70 | +```js |
| 71 | +var search = function(nums, target) { |
| 72 | + let start = 0, mid = 0, end = nums.length-1; |
| 73 | + while(start <= end){ |
| 74 | + mid = start + ((start+end)>>2); |
| 75 | + if(nums[mid] === target){ |
| 76 | + return mid; |
| 77 | + } |
| 78 | + if(nums[mid] > target){ |
| 79 | + end = mid - 1; |
| 80 | + } else { |
| 81 | + start = mid + 1; |
| 82 | + } |
| 83 | + } |
| 84 | + return -1; |
| 85 | +}; |
| 86 | +``` |
| 87 | + |
| 88 | +JS 用 indexOf 也可以: |
| 89 | + |
| 90 | +```js |
| 91 | +var search = function(nums, target) { |
| 92 | + return nums.indexOf(target); |
| 93 | +}; |
| 94 | +``` |
| 95 | + |
| 96 | +##### 34.在排序数组中查找元素的第一个和最后一个位置 [find-first-and-last-position-of-element-in-sorted-array](https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/) |
| 97 | + |
| 98 | +给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 |
| 99 | + |
| 100 | +如果数组中不存在目标值 target,返回 [-1, -1]。 |
| 101 | + |
| 102 | +**进阶:** |
| 103 | + |
| 104 | +- 你可以设计并实现时间复杂度为 `O(log n)` 的算法解决此问题吗? |
| 105 | + |
| 106 | +> 输入:nums = [5,7,7,8,8,10], target = 8 |
| 107 | +> 输出:[3,4] |
| 108 | +
|
| 109 | +```js |
| 110 | +var searchRange = function(nums, target) { |
| 111 | + if(target<nums[0] || target>nums[nums.length-1]){ |
| 112 | + return [-1,-1]; |
| 113 | + } |
| 114 | + let res = [-1,-1]; |
| 115 | + let left = 0, mid = 0, right = nums.length-1; |
| 116 | + while(left <= right){ |
| 117 | + mid = left + ((right-left) >> 1); |
| 118 | + if(nums[mid] >= target){ |
| 119 | + if(mid === 0 || (nums[mid] === target && nums[mid-1]<target)){ |
| 120 | + res[0] = mid; |
| 121 | + break; |
| 122 | + }else{ |
| 123 | + right = mid - 1; |
| 124 | + } |
| 125 | + }else{ |
| 126 | + left = mid + 1; |
| 127 | + } |
| 128 | + } |
| 129 | + if(res[0]!==-1 && nums[res[0]]===target){ |
| 130 | + if(res[0]===nums.length-1){ |
| 131 | + res[1] = res[0]; |
| 132 | + } else { |
| 133 | + for(let i=res[0]+1;i<nums.length;i++){ |
| 134 | + if(nums[i] !== target){ |
| 135 | + res[1] = i-1; |
| 136 | + break; |
| 137 | + } |
| 138 | + if(i===nums.length-1 && nums[i]===target){ |
| 139 | + res[1] = i; |
| 140 | + } |
| 141 | + } |
| 142 | + } |
| 143 | + } |
| 144 | + return res; |
| 145 | +}; |
| 146 | +``` |
| 147 | + |
| 148 | +```js |
| 149 | +var searchRange = function(nums, target) { |
| 150 | + return [nums.indexOf(target),nums.lastIndexOf(target)]; //使用内置方法,运行更快 |
| 151 | +}; |
| 152 | +``` |
| 153 | + |
0 commit comments