算法练习-二分查找(二)
文章目录
1 二分查找
链接:https://leetcode.cn/problems/binary-search
1.1 题目
给定一个 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]之间。
1.2 题解
class Solution {
public int search(int[] nums, int target) {
int low = 0;
int n = nums.length;
int high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] > target) high = mid - 1;
else low = mid + 1;
}
return -1;
}
}
2 猜数字大小
链接:https://leetcode.cn/problems/guess-number-higher-or-lower
2.1 题目
猜数字游戏的规则如下:
每轮游戏,我都会从 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
2.2 题解
/**
* Forward declaration of guess API.
* @param num your guess
* @return -1 if num is higher than the picked number
* 1 if num is lower than the picked number
* otherwise return 0
* int guess(int num);
*/
public class Solution extends GuessGame {
public int guessNumber(int n) {
int low = 1;
int high = n;
while(low <= high) {
int mid = low + (high - low) / 2;
int ret = guess(mid);
if (ret == 0) {
return mid;
} else if (ret == -1) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
}
3 寻找比目标字母大的最小字母
链接:https://leetcode.cn/problems/find-smallest-letter-greater-than-target
3.1 题目
给你一个字符数组 letters,该数组按非递减顺序排序,以及一个字符 target。letters 里至少有两个不同的字符。
返回 letters 中大于 target 的最小的字符。如果不存在这样的字符,则返回 letters 的第一个字符。
示例 1:
输入: letters = [“c”, “f”, “j”],target = “a”
输出: “c”
解释:letters 中字典上比 ‘a’ 大的最小字符是 ‘c’。
示例 2:
输入: letters = [“c”,“f”,“j”], target = “c”
输出: “f”
解释:letters 中字典顺序上大于 ‘c’ 的最小字符是 ‘f’。
示例 3:
输入: letters = [“x”,“x”,“y”,“y”], target = “z”
输出: “x”
解释:letters 中没有一个字符在字典上大于 ‘z’,所以我们返回 letters[0]。
提示:
2 <= letters.length <= 104
letters[i] 是一个小写字母
letters 按非递减顺序排序
letters 最少包含两个不同的字母
target 是一个小写字母
3.2题解
class Solution {
public char nextGreatestLetter(char[] letters, char target) {
int n = letters.length;
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (letters[mid] > target) {
if (mid == 0 || letters[mid - 1] <= target) {
return letters[mid];
} else {
high = mid - 1;
}
} else {
low = mid + 1;
}
}
return letters[0];
}
}
4 搜索插入位置
链接:https://leetcode.cn/problems/search-insert-position
4.1 题目
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 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
4.2 题解
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if(nums[mid] >= target) {
if (mid == 0 || nums[mid - 1] < target) {
return mid;
} else {
high = mid - 1;
}
} else {
low = mid + 1;
}
}
return n;
}
}
5 在排序数组中查找元素的第一个和最后一个位置
链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array
5.1 题目
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109
5.2 题解
class Solution {
public int[] searchRange(int[] nums, int target) {
int low = 0;
int n = nums.length;
int high = n - 1;
int left = -1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == target) {
if (mid == 0 || nums[mid - 1] != target) {
left = mid;
break;
} else {
high = mid - 1;
}
} else if (nums[mid] > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
low = 0;
int right = - 1;
high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == target) {
if (mid == n - 1 || nums[mid + 1] != target) {
right = mid;
break;
} else {
low = mid + 1;
}
} else if (nums[mid] > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return new int[] {
left, right};
}
}
6 稀疏数组搜索
链接:https://leetcode.cn/problems/sparse-array-search-lcci
6.1 题目
稀疏数组搜索。有个排好序的字符串数组,其中散布着一些空字符串,编写一种方法,找出给定字符串的位置。
示例1:
输入: words = [“at”, “”, “”, “”, “ball”, “”, “”, “car”, “”, “”,“dad”, “”, “”], s = “ta”
输出:-1
说明: 不存在返回-1。
示例2:
输入:words = [“at”, “”, “”, “”, “ball”, “”, “”, “car”, “”, “”,“dad”, “”, “”], s = “ball”
输出:4
提示:
words的长度在[1, 1000000]之间
6.2 题解
class Solution {
public int findString(String[] words, String s) {
int low = 0;
int n = words.length;
int high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (words[mid].equals(s)) {
return mid;
} else if (words[mid].equals("")) {
if(words[low].equals(s)) return low;
else low++;
} else if (words[mid].compareTo(s) < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
}
7 搜索旋转排序数组
链接:https://leetcode.cn/problems/search-in-rotated-sorted-array
7.1 题目
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:
输入:nums = [1], target = 0
输出:-1
提示:
1 <= nums.length <= 5000
-104 <= nums[i] <= 104
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-104 <= target <= 104
7.2 题解
class Solution {
public int search(int[] nums, int target) {
int n = nums.length;
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == target) return mid;
else if (nums[low] <= nums[mid]) {
if (target >= nums[low] && target < nums[mid]) {
high = mid - 1;
} else {
low = mid + 1;
}
} else {
if (target > nums[mid] && target <= nums[high]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
}
return -1;
}
}
8 寻找旋转数组中的最小值
链接:https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array
8.1 题目
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
示例 2:
输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。
示例 3:
输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。
提示:
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
nums 中的所有整数 互不相同
nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转
8.2 题解
class Solution {
public int findMin(int[] nums) {
int n = nums.length;
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (low == high) {
return nums[mid];
}
if (mid != 0 && nums[mid] < nums[mid - 1]
|| (mid == 0 && nums[mid] < nums[high])) {
return nums[mid];
} else if (nums[mid] > nums[high]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
}
9 山脉数组的峰顶索引
链接:https://leetcode.cn/problems/peak-index-in-a-mountain-array
9.1 题目
符合下列属性的数组 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 是一个山脉数组
9.2 题解
class Solution {
public int peakIndexInMountainArray(int[] arr) {
int n = arr.length;
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (mid == 0) {
low = mid + 1;
} else if (mid == n - 1) {
high = mid - 1;
} else if (arr[mid] > arr[mid - 1] && arr[mid] > arr[mid + 1]) {
return mid;
} else if (arr[mid] > arr[mid - 1]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
}
10有效的完全平方数
链接:https://leetcode.cn/problems/valid-perfect-square
10.1 题目
给你一个正整数 num 。如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。
不能使用任何内置的库函数,如 sqrt 。
示例 1:
输入:num = 16
输出:true
解释:返回 true ,因为 4 * 4 = 16 且 4 是一个整数。
示例 2:
输入:num = 14
输出:false
解释:返回 false ,因为 3.742 * 3.742 = 14 但 3.742 不是一个整数。
提示:
1 <= num <= 231 - 1
10.2 题解
class Solution {
public boolean isPerfectSquare(int num) {
int low = 1;
int high = num;
while (low <= high) {
int mid = low + (high - low) / 2;
long mid2 = (long)mid * mid;
if (mid2 == num) {
return true;
} else if (mid2 > num) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return false;
}
}
11 搜索二维矩阵
链接:https://leetcode.cn/problems/search-a-2d-matrix
11.1 题目
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
示例 1:
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true
示例 2:
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-104 <= matrix[i][j], target <= 104
11.2 题解
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
int low = 0;
int high = m * n - 1;
int mid, midValue;
while (low <= high) {
mid = (low + high) / 2;
midValue = matrix[mid / n][mid % n];
if (target == midValue) {
return true;
} else if (target < midValue) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return false;
}
}