2022年4月10日
算法基础第一天
这题回顾一下二分法,要找最左和最右的边界,在mid = target之后,继续移动mid的位置即可。
/** 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] 提示: 0 <= nums.length <= 10^5 -10^9 <= nums[i] <= 10^9 nums 是一个非递减数组 -10^9 <= target <= 10^9 * @author zeroheart * */ public class Question34 { public int[] searchRange(int[] nums, int target) { int[] res = new int[]{-1,-1}; res[0] = binarySearch(nums, target, 1); res[1] = binarySearch(nums, target, 0); return res; } public int binarySearch(int[] nums, int target, int left){ int res = -1; int low = 0; int high = nums.length - 1; int mid = 0; while (low <= high){ mid = low + (high-low) / 2; if(nums[mid]<target){ low = mid + 1; }else if(nums[mid]>target){ high = mid - 1; }else { // 虽然已经找到了,不过不一定是最左边的或者最右边的 res = mid; if(left == 1){ // 如果要找的是最左的,右边的指针继续减小。 high = mid - 1; }else { low = mid + 1; } } } return res; } public int[] searchRange1(int[] nums, int target) { // 通过了,但是没用到算法 int[] res = new int[]{-1,-1}; for(int i = 0; i<nums.length; i++){ if(nums[i] == target && res[0] == -1){ res[0] = i; } if(nums[i] == target && res[0] != -1 && i == nums.length - 1){ res[1] = i; break; } if(nums[i] > target && res[0] != -1){ res[1] = i-1; break; } } if(res[0] == -1){ res[1] = -1; } if(res[1] < res[0]){ res[1] = res[0]; } return res; } }
旋转排序数组,nums[0]是一个分界点。这题也是利用二分法,确定是在高的一侧还是低的一层之后,在比较target的大小来移动指针。
/**
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 。
示例 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
-10^4 <= nums[i] <= 10^4
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-10^4 <= target <= 10^4
进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗?
* @author zeroheart
*
*/
public class Question33 {
public int search(int[] nums, int target) {
int res = -1;
int low = 0;
int high = nums.length - 1;
int mid = 0;
while (low <= high) {
mid = low + (high - low) / 2;
if (nums[mid] == target) {
return mid;
}
// nums[0] 是一个分界点
if (nums[0] <= nums[mid]) {
// mid 出现在高的一侧
// 看target的情况,移动指针
if (nums[0] <= target && target < nums[mid]) {
// 注意这里的<=
high = mid - 1;
} else {
low = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[nums.length - 1]) {
// 注意这里的<=
low = mid + 1;
} else {
high = mid - 1;
}
}
}
return res;
}
}
这也是二分法的变种,二位数组的坐标要转一下。注意二分的high = len -1;
/**
74. 搜索二维矩阵
编写一个高效的算法来判断 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
* @author zeroheart
*
*/
public class Question74 {
public boolean searchMatrix(int[][] matrix, int target) {
// 实际就是二分法,就是要做一次坐标转化
int m = matrix.length;
int n = matrix[0].length;
int low = 0; int high = m * n - 1;
while (low <= high){
int mid = low + (high - low) / 2;
int tmp = matrix[mid/n][mid%n];
if(tmp == target) return true;
if(tmp < target) {
low = mid + 1;
}else {
high = mid - 1;
}
}
return false;
}
}