- 寻找无重复旋转数组中数字的位置
tip: 循环不变量:目标数字永远出现在[start, end]之间
解法: 比较middle和start. 如果middle大于等于start, 那么middle在前半段。反之后半段。
如果middle大于等于start: target大于等于start且小于middle,那么前半段。反之后半段。
如果middle小于start: target大于middle且小于等于end,那么后半段。否则前半段。
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
let start = 0, end = nums.length - 1;
while (start <= end) {
let middle = Math.floor((start + end) / 2);
if (target == nums[middle]) return middle;
if (nums[middle] >= nums[start]) {
if (target >= nums[start] && target < nums[middle]) {
end = middle - 1;
} else {
start = middle + 1;
}
} else {
if (target > nums[middle] && target <= nums[end]) {
start = middle + 1;
} else {
end = middle - 1;
}
}
}
return -1;
};
-
还能以寻找目标为二分查找的范围对象
-
寻找峰值
tip: 根据middle处于上升周期还是下降周期判断
/**
* @param {number[]} nums
* @return {number}
*/
//循环不变量:要查找的数永远在[start, end]内
var findPeakElement = function(nums) {
if (nums.length == 1) return 0;
let start = 0, end = nums.length - 1;
while (start <= end) {
let middle = Math.floor((start + end) / 2);
if (middle == 0) {
if (nums[middle] > nums[middle + 1]) {
return middle;
} else {
start = middle + 1;
}
} else if (middle == nums.length - 1) {
if (nums[middle] > nums[middle - 1]) {
return middle;
} else {
end = middle - 1;
}
} else {
if (nums[middle] > nums[middle - 1] && nums[middle] > nums[middle + 1]) {
return middle;
} else if (nums[middle] < nums[middle + 1]) {
start = middle + 1;
} else {
end = middle - 1;
}
}
}
return -1;
};
- 包含重复数字的旋转数组二分查找
/**
* @param {number[]} nums
* @param {number} target
* @return {boolean}
*/
var search = function(nums, target) {
let start = 0, end = nums.length - 1;
//循环不变量为,要找的值总在[start, end]
while (start <= end) {
let middle = Math.floor((start + end) / 2);
if (nums[middle] == target) return true;
//middle一定在左半边
if (nums[middle] > nums[start]) {
if (target >= nums[start] && target < nums[middle]) {
end = middle - 1;
} else {
start = middle + 1;
}
//middle一定在右半边
} else if (nums[middle] < nums[start]) {
//target也在右半边
if (target > nums[middle] && target <= nums[end]) {
start = middle + 1;
} else {
end = middle - 1;
}
//middle恰好等于start,两种可能,一种是在左边,一种是旋转到了右边
//那么这时候,判断它是不是一定在左边
} else {
//Middle有可能左有可能右可左可右
if (nums[start] == nums[end]) {
//把头部和尾部重复的数字都去掉,反正都不等于target
//middle如果在旋转到右边的部分,那么目标在[start, middle - 1]
//middle如果在保留在左侧的部分,那么目标在[middle + 1, end]
while (start + 1 <= end && nums[start + 1] == nums[start]) start++;
//没有其他数字,return false
start++;
if (start >= end) return false;
while (end - 1 >= start && nums[end - 1] == nums[end]) end--;
end--;
//middle一定在左边
} else {
start = middle + 1;
}
}
}
return false;
};
- 排序数组中找到距离某个数x最近的k个数。
解法:找到x的位置,不断向两侧双指针扩散,直到大小为k
/**
* @param {number[]} arr
* @param {number} k
* @param {number} x
* @return {number[]}
*/
var binarySearch = function(arr, start, end, target) {
if (arr[start] >= target) return start - 1;
if (arr[end] <= target) return end + 1;
while(start <= end) {
let middle = Math.floor((start + end) / 2);
if (arr[middle] <= target && arr[middle + 1] >= target) {
return middle + 1;
} else if (arr[middle] < target) {
start = middle + 1;
} else {
end = middle - 1;
}
}
}
var findClosestElements = function(arr, k, x) {
//找到x的位置
let targetPos = binarySearch(arr, 0, arr.length - 1, x);
//如果等于-1, 证明小于最小值
if (targetPos == -1) {
return arr.slice(0, k);
//等于len,证明大于最大值
} else if (targetPos == arr.length) {
return arr.slice(arr.length - k, arr.length);
} else {
let i = targetPos - 1, j = targetPos, result = [];
while(k > 0 && i >= 0 && j < arr.length) {
if (Math.abs(arr[i] - x) <= Math.abs(arr[j] - x)) {
result.unshift(arr[i]);
i--;
} else {
result.push(arr[j]);
j++;
}
k--;
}
while (k > 0) {
if (i == -1) {
while(k > 0) {
result.push(arr[j]);
j++;
k--;
}
} else if (j == arr.length) {
while(k > 0) {
result.unshift(arr[i]);
i--;
k--;
}
}
}
return result;
}
};