二分查找
[744. 寻找比目标字母大的最小字母](简单)
题目描述:
给你一个排序后的字符列表 letters ,列表中只包含小写英文字母。另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母。
在比较时,字母是依序循环出现的。举个例子:
- 如果目标字母 target = 'z' 并且字符列表为 letters = ['a', 'b'],则答案返回 'a'
注: 若字符列表中没有比目标字母大的,则返回列表中第一个字母
示例 1:
输入: letters = ["c", "f", "j"],target = "a"
输出: "c"
示例 2:
输入: letters = ["c","f","j"], target = "c"
输出: "f"
示例 3:
输入: letters = ["c","f","j"], target = "d"
输出: "f"
提示:
2 <= letters.length <= 104
letters[i]
是一个小写字母letters
按非递减顺序排序letters
最少包含两个不同的字母target
是一个小写字母
解题方法
方法一:按顺序查找
已知给定的数组是按照递增的顺序排序,因此可以通过遍历列表,从左往右开始查找第一个比目标字母大的字母,即为比目标字母大的最小字母
var nextGreatestLetter = function (letters, target) {
const length = letters.length;
//先将列表的第一个字母赋给result
let result = letters[0];
for (let i = 0; i < length; i++) {
//当某一个列表字母大于目标字母,将该字母赋给result
if (letters[i] > target) {
result = letters[i];
break;
}
}
//返回结果
return result;
};
方法二:二分查找
因为列表是有序的,适合使用二分法来进行查找
-
首先比较目标字母和列表的最后一个字母,如果目标字母大于或等于列表的最后一个字母,则列表中就不存在比目标字母大的字母,此时返回列表的第一个字母;反之,列表中一定存在比目标字母大的字母;
-
进行二分查找,初始时,二分查找的范围是整个列表的长度-1,接着取列表的中间值与目标字母进行比较,如果中间字母大于目标字母,则从左侧继续查找,反之从右侧继续查找
var nextGreatestLetter = function (letters, target) {
const length = letters.length;
//第一步判断目标字母与列表的最后一个字母
if (target >= letters[length - 1]) {
return letters[0];
}
//定义二分查找的开始和结尾
let low = 0, high = length - 1;
while (low < high) {
//定义中间下标值
const mid = Math.floor((high - low) / 2) + low;
//中间字母大于目标字母,将中间下标当做结尾,从左侧接着查找
if (letters[mid] > target) {
high = mid;
//反之,将中间下标+1当做开始,从右侧接着查找
} else {
low = mid + 1;
}
}
//返回结果
return letters[low];
};
704. 二分查找(简单)
题目描述:
给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
提示:
- 你可以假设
nums
中的所有元素是不重复的。 n
将在[1, 10000]
之间。nums
的每个元素都将在[-9999, 9999]
之间。
解题方法
使用二分法进行查找,开始时查找的范围是整个数组,然后每次查找都取中间位置的值与目标值进行比较,如果中间值大于目标值说明目标值在左侧区域,接着取左侧的中间值继续与目标值比较,直到开始位置小于等于结束位置时停止查找
在升序数组 nums
中寻找目标值target
,对于特定下标 i
,比较 nums[i]
和 target
的大小:
-
如果
nums[i]=target
,则下标i
即为要寻找的下标; -
如果
nums[i]>target
,则target
只可能在下标i
的左侧; -
如果
nums[i]<target
,则target
只可能在下标i
的右侧。
var search = function(nums, target) {
const length = nums.length
//声明左边开始位置和右侧结束位置
let left = 0 ,right= length-1
while(left<=right){
//取两者的中间值
const mid = Math.floor((right - left) / 2) + left;
//如果中间值等于目标值,返回该值下标
if(nums[mid]==target) {
return mid
//如果中间值大于目标值,则缩小范围即中间值的左侧范围
}else if (nums[mid]>target){
right=mid-1
//如果中间值小于目标值,则缩小范围即中间值的右侧范围
}else{
left=mid+1
}
}
//循环完依旧没有,则返回-1
return -1
};
374. 猜数字大小(简单)
题目描述:
猜数字游戏的规则如下:
-
每轮游戏,我都会从 1 到 n 随机选择一个数字。 请你猜选出的是哪个数字。
-
如果你猜错了,我会告诉你,你猜测的数字比我选出的数字是大了还是小了。 你可以通过调用一个预先定义好的接口
int guess(int num)
来获取猜测结果,返回值一共有 3 种可能的情况(-1
,1
或0
): -
-1:我选出的数字比你猜的数字小
pick < num
-
1:我选出的数字比你猜的数字大
pick > num
-
0:我选出的数字和你猜的数字一样。恭喜!你猜对了!
pick == num
返回我选出的数字。
示例 1:
输入:n = 10, pick = 6
输出:6
示例 2:
输入:n = 1, pick = 1
输出:1
示例 3:
输入:n = 2, pick = 1
输出:1
示例 4:
输入:n = 2, pick = 2
输出:2
提示:
- 1 <= n <= 231 - 1
- 1 <= pick <= n
解题方法:
开始位置为1,结束位置为n,查找的前提条件是开始位置小于结束位置;将两者的中间值进行判断,当返回的值<=0时,说明目标值在中间值的左侧部分,反之目标值在右侧,直到最后开始位置与结束位置相同时,说明已找到该值,此时退出循环
var guessNumber = function(n) {
//定义开始位置和结束位置
let left = 1,right=n;
while(left<right){
//取两者中间值
const mid = Math.floor((right - left)/2 + left)
//判断中间值所返回的值,如果<=0,则说明值在左侧区域
if(guess(mid)<=0){
right=mid
//反之在右侧区域
}else{
left=mid+1
}
}
//当开始位置与结束位置相同时,区间只有一个值即为答案,退出循环
return left
};
852. 山脉数组的峰顶索引
题目描述
符合下列属性的数组 arr
称为 山脉数组 :
arr.length >= 3
存在 i
(0 < i < arr.length - 1) 使得:
arr[0] < arr[1] < ... arr[i-1] < arr[i]
arr[i] > arr[i+1] > ... > arr[arr.length - 1]
给你由整数组成的山脉数组 arr
,返回任何满足 arr[0] < arr[1] < ... arr[i - 1] < arr[i] > arr[i + 1] > ... > arr[arr.length - 1]
的下标 i 。
示例 1:
输入:arr = [0,1,0]
输出:1
示例 2:
输入:arr = [0,2,1,0]
输出:1
示例 3:
输入:arr = [0,10,5,2]
输出:1
示例 4:
输入:arr = [3,4,5,1]
输出:2
示例 5:
输入:arr = [24,69,100,99,79,78,67,36,26,19]
输出:2
提示:
- 3 <= arr.length <= 104
- 0 <= arr[i] <= 106
- 题目数据保证 arr 是一个山脉数组
解题方法
从题目描述中可以得知,山脉数组是中间值为最大,两边逐渐减小的数组,因此取得中间值来与其左右值进行比较
-
中间值大于左侧值并且大于右侧值,则返回该中间值的下标
-
中间值大于左侧且小于右侧,则结果值在右侧
-
中间值大于右侧且小于左侧,则结果值在左侧
var peakIndexInMountainArray = function (arr) {
const length = arr.length;
let left = 0,
right = length - 1;
while (left < right) {
const mid = Math.floor((right - left) / 2 + left);
if (arr[mid] > arr[mid - 1] && arr[mid] > arr[mid + 1]) {
return mid;
} else if (arr[mid] > arr[mid - 1] && arr[mid] < arr[mid + 1]) {
left = mid;
} else {
right = mid + 1;
}
}
return left;
};
35. 搜索插入位置
题目描述
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
提示:
- 1 <= nums.length <= 104
- -104 <= nums[i] <= 104
- nums 为 无重复元素 的 升序 排列数组
- -104 <= target <= 104
解题方法
-
特殊情况:判断目标值是否大于数组最后一个值,true则返回数组长度
-
开始范围为
[left,right]
,当中间值小于目标值时,结果值在右侧,此时范围为[mid+1,right]
,反之范围为[left,mid]
var searchInsert = function (nums, target) {
const length = nums.length;
let low = 0,
high = length - 1;
if (target > nums[high]) {
return length;
}
while (low < high) {
const mid = Math.floor((high - low) / 2 + low);
if (nums[mid] < target) {
low = mid + 1;
} else {
high = mid;
}
// return mid
}
return low;
};