- 写于2019年5月24日
33. 搜索旋转排序数组
① 题目描述
- 假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组
[0,1,2,4,5,6,7]
可能变为[4,5,6,7,0,1,2]
)。 - 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -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
② 不追求O(log n)
,线性扫描
- 直接遍历,有返回对应的下标,没有返回-1。
- 代码如下:
public int search(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
if (nums[i] == target) {
return i;
}
}
return -1;
}
③ 数组从任意位置劈开是有序的
- 算法基于一个事实,数组从任意位置劈开后,至少有一半是有序的,什么意思呢?比如
[ 4 5 6 7 1 2 3]
,从 7 劈开,左边是[ 4 5 6 7]
,右边是[ 7 1 2 3]
,左边是有序的。 - 先查找数组从mid劈开是否有序,即
nums[start] <= nums[mid]
,这表明左半段有序,否则右半段有序。 - 代码如下:
public int search(int[] nums, int target) {
int start = 0, end = nums.length - 1;
while (start <= end) {
int mid = (start + end) / 2;
if (nums[mid] == target) {
return mid;
}
if (nums[start] <= nums[mid]) {// 左半段有序
if (target >= nums[start] && target <= nums[mid]) {// 左半段升序,比较下标start和mid
end = mid - 1; // target在左半段,更新end
} else {
start = mid + 1;// target不在左半段,更新start
}
} else {// 右半段有序
if (target >= nums[mid] && target <= nums[end]) {// 右半段升序,比较下标mid和end
start = mid + 1;//target在右半段,更新start
} else {
end = mid - 1;//target不在右半段,更新end
}
}
}
return -1;
}
34. 在排序数组中查找元素的第一个和最后一个位置
① 题目描述
- 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
- 你的算法时间复杂度必须是
O(log n)
级别。 - 如果数组中不存在目标值,返回
[-1, -1]
。 - 示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
- 示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
② 不追求O(log n)
,线性扫描
- 直接遍历数组,时间复杂读
O(n)
,空间复杂度O(1)
。 - 代码如下:
public int[] searchRange(int[] nums, int target) {
int[] result = {-1, -1};
for (int i = 0; i < nums.length; i++) {
if (nums[i] == target) {
if (result[0] == -1) {
result[0] = i;
} else {
result[1] = i;
}
}
}
if (result[0] != -1 && result[1] == -1) {
result[1] = result[0];
}
return result;
}
③ 二分查找
- 当
target == nums[mid]
时,在左半部分查找可能的result[0]
。查找到的start越界或者nums[start] != target
,直接返回[-1, -1]
;否则,更新result[0]
。 - 当
target == nums[mid]
时,在右半部分查找可能的result[1]
,查找完成后,用end更新result[1]
。
public int[] searchRange(int[] nums, int target) {
int[] result = {-1, -1};
int start = 0;
int end = nums.length - 1;
while (start <= end) {//查找start
int mid = (start + end) / 2;
if (target == nums[mid]) {
end = mid - 1;
} else if (target < nums[mid]) {
end = mid - 1;
} else {
start = mid + 1;
}
}
//考虑 tartget 是否存在,判断我们要找的值是否等于 target 并且是否越界
if (start == nums.length || nums[start] != target) {
return result;
}
result[0] = start;
start = 0; end = nums.length - 1;
while (start <= end) {//查找end
int mid = (start + end) / 2;
if (target==nums[mid]){
start=mid+1;
}else if (target<nums[mid]){
end=mid-1;
}else {
start=mid+1;
}
}
result[1]=end;
return result;
}