文章目录
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
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-104 <= matrix[i][j], target <= 104
思路:
首先,题目要求查询target是否在矩阵中,同时矩阵是有序存储的,从左到右,从上到下增大,即右下 > 左上。
target的状态:
情况一、 target在矩阵外,即小于矩阵最小值,大于矩阵最大值。
情况二、target处于矩阵范围内,同时是矩阵内元素,返回true。
情况三、target处于矩阵范围内,但不是矩阵内元素,返回false。
思路:①、我们先遍历矩阵的行,确认target所处的范围是在哪一行,使用idx记录。
②、我们对该行使用二分法,求出target的具体位置。
二分具体实现:
方法1:
-
我们使用全开区间,即(l, r) 表示范围,
-
令 l = -1, r = n,即恰好在矩阵长度左右范围 + 1,因为是开区间,不包含l和r本身。
-
同时在循环中加入判断 if(r - l <= 1) break; 因为要判断target恰好在两个数之间,不是矩阵元素的情况。
代码如下:
题解:
java
//方法1
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
if(target < matrix[0][0] || target > matrix[m - 1][n - 1]) return false;
int idx = 0;
for(int i = 0; i < m; i ++ ) {
if(matrix[i][n - 1] < target) continue;
else if(matrix[i][n - 1] >= target) {
idx = i;
break;
}
}
// 左开右开区间()
int l = -1, r = n;
boolean flag = false;
while(l < r) {
int mid = (l + r) / 2;
if(matrix[idx][mid] > target) r = mid;
else if(matrix[idx][mid] < target) l = mid;
else {
flag = true;
break;
}
if(r - l <= 1) break;
}
return flag;
}
方法2:
-
使用我们使用全闭区间,即[l, r] 表示区间范围。
-
令l = 0, r = n - 1。因为是闭区间,我们需要包含l和r本身。
-
闭区间不需要添加判断,因为l和r有 + 1和 - 1的权值变化,最终会自己跳出循环。
代码如下:
java
int l = 0, r = n - 1;
boolean flag = false;
while(l <= r) {
int mid = (l + r) / 2;
if(matrix[idx][mid] > target) r = mid - 1;
else if(matrix[idx][mid] < target) l = mid + 1;
else {
flag = true;
break;
}
}
return flag;
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
提示:
1 <= nums.length <= 5000
-104 <= nums[i] <= 104
nums
中的每个值都 独一无二- 题目数据保证
nums
在预先未知的某个下标上进行了旋转 -104 <= target <= 104
思路:
- 就以[4, 5, 6, 7, 0, 1, 2], target = 8 举例
我们可以明显的看出,如果想要使用二分,就需要找有序的数组,此时我们看到,
-
该数组有两段局部有序的数组。
-
nums[0]一定大于第二段有序数组的所有数。
我们可以根据第二条性质,使得每次判断都是在有序数组中。
- 我们实现二分的判断依据就成了,target是否在当前有序数组中,如果不在,那么肯定在该有序数组右(左)边
最开始判断,相等,就直接返回mid.
然后,判断if(nums[0] <= nums[mid])。
- 这样规定的mid一定处于第一段有序数组中,
- 内部继续判断if(nums[0] <= target && target < nums[mid]) r = mid - 1
- else 说明target位于更右边 l = mid + 1
其次,else中
- 这样的mid一定处于第二段有序数组中,
- 判断if(nums[mid] < target && target <= nums[n - 1]) l = mid + 1;
- else 说明target位于更左边 r = mid - 1;
题解:
java
public int search(int[] nums, int target) {
if(nums.length == 1) return (nums[0] == target) ? 0 : -1;
int n = nums.length;
int l = 0, r = n - 1;
while(l <= r) {
int mid = (l + r) / 2;
if(nums[mid] == target) return mid;
if(nums[0] <= nums[mid]) {
if(nums[0] <= target && target < nums[mid]) r = mid - 1;
else l = mid + 1;
} else {
if(nums[mid] < target && target <= nums[n - 1]) l = mid + 1;
else r = mid - 1;
}
}
return -1;
}