1. 搜索插入位置
leetcode题目链接: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
解法:
java
class Solution {
/**
* 在已排序数组中查找目标值的位置,若不存在则返回应插入的位置
* @param nums 已按升序排列的整型数组
* @param target 需要查找的目标值
* @return 目标值的索引(存在时)或应插入的位置(不存在时)
*/
public int searchInsert(int[] nums, int target) {
// 初始化二分查找的左右边界指针
int low = 0;
int high = nums.length - 1;
// 标准二分查找框架
while (low <= high) {
// 计算中间位置,避免整数溢出
int mid = low + (high - low) / 2; // 等同于 (low + high)/2
if (nums[mid] == target) {
// 找到目标值,直接返回索引
return mid;
} else if (nums[mid] < target) {
// 中间值小于目标值,说明目标在右半区间
// 调整左边界(排除中间位置)
low = mid + 1;
} else {
// 中间值大于目标值,说明目标在左半区间
// 调整右边界(排除中间位置)
high = mid - 1;
}
}
// 当循环结束时,low指针的位置表示:
// 1. 数组中第一个大于target的元素位置
// 2. 当target大于所有元素时,low指向数组末尾(nums.length)
// 这正是target应该插入的位置
return low;
}
}
2. 搜索二维矩阵
leetcode题目链接:74. 搜索二维矩阵
给你一个满足下述两条属性的
m x n整数矩阵:
- 每行中的整数从左到右按非严格递增顺序排列。
- 每行的第一个整数大于前一行的最后一个整数。
给你一个整数
target,如果target在矩阵中,返回true;否则,返回false。
示例 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
解法:
java
class Solution {
/**
* 解法一:先二分查找所在的行,再二分查找所在的列
*/
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
// 先二分查找锁定在哪一行
int row = -1;
int startRow = 0;
int endRow = m-1;
while(startRow <= endRow){
int midRow = (startRow+endRow)/2;
if(target >= matrix[midRow][0] && target <= matrix[midRow][n-1]){
row = midRow;
}
if(target < matrix[midRow][0]){
endRow = midRow-1;
}else{
startRow = midRow+1;
}
}
if(row == -1){
return false;
}
// 锁定行之后,在指定行二分查找对应的列
int col = -1;
int startCol = 0;
int endCol = n-1;
while(startCol <= endCol){
int midCol = (startCol+endCol)/2;
if(target == matrix[row][midCol]){
return true;
}
if(target < matrix[row][midCol]){
endCol = midCol-1;
}else{
startCol = midCol+1;
}
}
return false;
}
/**
* 解法二:把二位矩阵转换为一维大矩阵,再二分查找
*/
public boolean searchMatrix1(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return false;
}
int m = matrix.length;
int n = matrix[0].length;
int left = 0;
int right = m * n - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
int row = mid / n;
int col = mid % n;
int midValue = matrix[row][col];
if (midValue == target) {
return true;
} else if (midValue < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
}
3. 在排序数组中查找元素的第一个和最后一个位置
leetcode题目链接:34. 在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组
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]
解法:
java
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] result = new int[]{-1, -1};
if (nums == null || nums.length == 0) {
return result;
}
result[0] = findLeftBound(nums, target);
result[1] = findRightBound(nums, target);
return result;
}
// 寻找目标值的左边界
private int findLeftBound(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int index = -1;
while (left <= right) {
int mid = left + (right - left) / 2; // 防止整数溢出
if (nums[mid] == target) {
index = mid; // 记录当前位置,继续向左寻找更小的索引
right = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return index;
}
// 寻找目标值的右边界
private int findRightBound(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int index = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
index = mid; // 记录当前位置,继续向右寻找更大的索引
left = mid + 1;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return index;
}
}
4. 搜索旋转排序数组
leetcode题目链接:33. 搜索旋转排序数组
整数数组
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
解法:
java
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
}
// 判断左半部分是否有序
if (nums[left] <= nums[mid]) {
// 如果目标在左半有序区间内
if (target >= nums[left] && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else { // 右半部分有序
// 如果目标在右半有序区间内
if (target > nums[mid] && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}
}
5. 寻找旋转排序数组中的最小值
leetcode题目链接:153. 寻找旋转排序数组中的最小值
已知一个长度为
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 次得到输入数组。
解法:
java
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
int result = nums[0];
while(left <= right){
int mid = left + (right - left)/2;
// 左半侧是有序的,找到左边的最小值,然后再去找右半侧
if(nums[left] <= nums[mid]){
result = Math.min(result, nums[left]);
left = mid+1;
// 右半侧是有序的,找到右边的最小值,然后再去找左半侧
}else{
result = Math.min(result, nums[mid]);
right = mid-1;
}
}
return result;
}
}
6. 寻找两个正序数组的中位数
leetcode题目链接:4. 寻找两个正序数组的中位数
给定两个大小分别为
m和n的正序(从小到大)数组nums1和nums2。请你找出并返回这两个正序数组的 中位数 。算法的时间复杂度应该为
O(log (m+n))。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
解法:hard,没看懂
java
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int total = m + n;
if (total % 2 == 1) {
// 总长度为奇数,返回第 (total+1)/2 小的元素
return findKth(nums1, 0, nums2, 0, (total + 1) / 2);
} else {
// 总长度为偶数,返回第 total/2 和 total/2 +1 小的元素的平均值
int left = findKth(nums1, 0, nums2, 0, total / 2);
int right = findKth(nums1, 0, nums2, 0, total / 2 + 1);
return (left + right) / 2.0;
}
}
// 辅助函数:找到两个有序数组中第k小的元素
private int findKth(int[] nums1, int i, int[] nums2, int j, int k) {
// 如果nums1的起始位置超过数组长度,说明nums1已全部排除,直接取nums2的第k个元素
if (i >= nums1.length) {
return nums2[j + k - 1];
}
// 同理处理nums2的情况
if (j >= nums2.length) {
return nums1[i + k - 1];
}
// 当k=1时,返回两个数组当前起始位置的较小值
if (k == 1) {
return Math.min(nums1[i], nums2[j]);
}
// 计算两个数组的中间位置,用于比较
int mid1 = i + k / 2 - 1;
int mid2 = j + k / 2 - 1;
// 处理越界情况,若越界则取最大值,确保排除另一数组的部分元素
int val1 = (mid1 < nums1.length) ? nums1[mid1] : Integer.MAX_VALUE;
int val2 = (mid2 < nums2.length) ? nums2[mid2] : Integer.MAX_VALUE;
// 比较中间值,排除较小值所在数组的前k/2个元素
if (val1 <= val2) {
// 排除nums1的前k/2个元素,更新起始位置和k值
return findKth(nums1, mid1 + 1, nums2, j, k - k / 2);
} else {
// 排除nums2的前k/2个元素
return findKth(nums1, i, nums2, mid2 + 1, k - k / 2);
}
}
}