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]之间。
答案:
在有序数组中查找目标值用二分法
对于下标mid(区间(i,j)的中点),
nums[mid] == target,nums[mid]就是目标值
nums[mid] > targer,目标值在(i, mid - 1)之间
nums[mid] < targer,目标值在(mid + 1, j)之间
代码如下:
class Solution {
public int search(int[] nums, int target) {
int n = nums.length;
int i = 0, j = n - 1;
while (i <= j) {
int mid = (j - i) / 2 + i;
if (nums[mid] == target)
return mid;
if (nums[mid] > target) {
j = mid - 1;
} else {
i = mid + 1;
}
}
return -1;
}
}
剑指 Offer II 070. 排序数组中只出现一次的数字
题目描述
给定一个只包含整数的有序数组 nums ,每个元素都会出现两次,唯有一个数只会出现一次,请找出这个唯一的数字。
你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。
示例 1:
输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2
示例 2:
输入: nums = [3,3,7,7,10,11,11]
输出: 10
提示:
1 <= nums.length <= 10^5
0 <= nums[i] <= 10^5
答案:
对于下标mid(区间(i,j)的中点),
如果mid是偶数,nums[mid] == nums[mid + 1]; mid + 1 == mid ^ 1;
如果mid是奇数,nums[mid] == nums[mid - 1]; mid - 1 == mid ^ 1;
符合上述条件,则目标值在(mid + 1,j)之间;
否则,目标值在(i,mid - 1)之间(出现一次数的后面的nums都不符合以上条件)。
代码如下:
class Solution {
public int singleNonDuplicate(int[] nums) {
int n = nums.length;
int i = 0, j = n - 1;
while (i <= j) {
int mid = (i + j) / 2;
if (nums[mid ^ 1] == nums[mid]) {
i = mid + 1;
} else {
j = mid - 1;
}
}
return nums[i];
}
}
剑指 Offer II 072. 求平方根
题目描述
给定一个非负整数 x ,计算并返回 x 的平方根,即实现 int sqrt(int x) 函数。
正数的平方根有两个,只输出其中的正数平方根。
如果平方根不是整数,输出只保留整数的部分,小数部分将被舍去。
示例 1:
输入: x = 4
输出: 2
示例 2:
输入: x = 8
输出: 2
解释: 8 的平方根是 2.82842…,由于小数部分将被舍去,所以返回 2
提示:
0 <= x <= 2^31 - 1
答案:
class Solution {
public int mySqrt(int x) {
int i = 0, j = x;
while (i <= j) {
int mid = (i + j) / 2;
if ((long)mid * mid <= x) {
i = mid + 1;
} else {
j = mid - 1;
}
}
return j;
}
}
剑指 Offer 11. 旋转数组的最小数字
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
示例 1:
输入:numbers = [3,4,5,1,2]
输出:1
示例 2:
输入:numbers = [2,2,2,0,1]
输出:0
提示:
n == numbers.length
1 <= n <= 5000
-5000 <= numbers[i] <= 5000
numbers 原来是一个升序排序的数组,并进行了 1 至 n 次旋转
答案:
对于下标mid(区间(i,j)的中点),
如果numbers[mid] < numbers[j],目标值在(i,mid)之间(mid也可能是目标值);
如果numbers[mid] > numbers[j],目标值在(mid + 1,j)之间(mid不可能是目标值);
如果numbers[mid] == numbers[j],不确定目标值的位置,则 j – (忽略右端点)。
class Solution {
public int minArray(int[] numbers) {
int n = numbers.length;
int i = 0, j = n - 1;
while (i <= j) {
int mid = (i + j) / 2;
if (numbers[mid] < numbers[j]) {
j = mid;
} else if (numbers[mid] > numbers[j]) {
i = mid + 1;
} else {
j--;
}
}
return numbers[i];
}
}
剑指 Offer 53 - I. 在排序数组中查找数字 I
题目描述
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
提示:
0 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9
nums 是一个非递减数组
-10^9 <= target <= 10^9
答案:
求出nums数组中目标值第一次和最后一次出现的位置l,r
出现的次数 = r - l + 1。
class Solution {
public int search(int[] nums, int target) {
int n = nums.length;
int i = 0, j = n - 1;
boolean flag = false;
int l = -1, r = -1;
while (i <= j) {
int mid = (i + j) / 2;
if (nums[mid] >= target) {
j = mid - 1;
} else {
i = mid + 1;
}
}
l = i;
i = 0;
j = n - 1;
while (i <= j) {
int mid = (i + j) / 2;
if (nums[mid] > target) {
j = mid - 1;
} else {
i = mid + 1;
}
}
r = j;
return r - l + 1;
}
}
剑指 Offer II 068. 查找插入位置
题目描述
给定一个排序的整数数组 nums 和一个整数目标值 target ,请在数组中找到 target ,并返回其下标。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 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
示例 4:
输入: nums = [1,3,5,6], target = 0
输出: 0
示例 5:
输入: nums = [1], target = 0
输出: 0
提示:
1 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10^4
nums 为无重复元素的升序排列数组
-10^4 <= target <= 10^4
答案:
第一个 >= 目标值所在的位置就是所求的位置
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int i = 0, j = n - 1;
while (i <= j) {
int mid = (i + j) / 2;
if (nums[mid] >= target) {
j = mid - 1;
} else {
i = mid + 1;
}
}
return i;
}
}
剑指 Offer II 069. 山峰数组的顶部
题目描述
符合下列属性的数组 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 = [1,3,5,4,2]
输出:2
示例 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 <= 10^4
0 <= arr[i] <= 10^6
题目数据保证 arr 是一个山脉数组
答案:
第一个arr[mid] > arr[mid + 1] 的位置
class Solution {
public int peakIndexInMountainArray(int[] arr) {
int n = arr.length;
int i = 0, j = n - 2;
while (i <= j) {
int mid = (i + j) / 2;
if (arr[mid] > arr[mid + 1]) {
j = mid - 1;
} else {
i = mid + 1;
}
}
return i;
}
}