数组翻转后形成的数组,如何在这个数组中查找某个数?如何查找最小值?
查找有序数组当然是二分数组最快了。但是现在这个有序数组经过了翻转,形成了两端有序数组。怎么办?
最好的思想,是把图画出来,再进行分析:
原始数组是一个递增(或非递减)数组,翻转数组则如右图所示。我们可以利用 nums[end] 作为切分参考点,判断每一次mid落在哪一段,然后再通过target和nums[mid], nums[start], nums[end]的关系,判断target是不是落在一段连续的有序区间,再决定下一次搜索向左还是向右。
在不断缩小搜索范围的时候,我们是有可能得到一段连续的有序数组的,很幸运的,按上面的思路写的代码,也能满足只有一段连续区间的情况。
问题1.1:在无重复的翻转数组中查找某个数,返回下标,or - 1。
int search(vector<int>& nums, int target) {
int start = 0;
int end = nums.size() - 1;
while (start <= end) {
int mid = start + ((end - start) >> 1);
if (nums[mid] == target) {
return mid; //查找到了,返回index
}
if (nums[mid] > nums[end]) { //mid在第一段
if (target < nums[mid] && target >= nums[start]) { //target在最左一段连续区间
end = mid - 1; //向左边缩小范围查找
}
else {
start = mid + 1; //向右边缩小范围查找
}
}
else { //mid在第二段
if (target > nums[mid] && target <= nums[end]) { //target在最右一段连续区间
start = mid + 1; //向右边缩小范围查找
}
else {
end = mid - 1; //向左边缩小范围查找
}
}
}
return -1;
}
问题1.2:在有重复的翻转数组中查找否个数,存在则返回true,反之false。
相比如无重复的翻转数组,有重复的情况要复杂一点,就是当 nums[mid] == 分界点 nums[end] 时,无法判断他是落在第一段还是第二段,但是我们知道
nums[mid] != target,则此时nums[end] !=target,那我们让 end--,直到这个分界点是一个开的点,而不是闭的点。之后的步骤和问题1.1一样。
bool search(vector<int>& nums, int target) {
int start = 0;
int end = nums.size() - 1;
while (start <= end) {
int mid = start + ((end - start) >> 1);
if (nums[mid] == target) {
return true;
}
if (nums[mid] > nums[end]) {
if (target < nums[mid] && target >= nums[start]) {
end = mid - 1;
}
else {
start = mid + 1;
}
}
else if (nums[mid] < nums[end]) {
if (target > nums[mid] && target <= nums[end]) {
start = mid + 1;
}
else {
end = mid - 1;
}
}
else {
end--;
}
}
return false;
}
问题2.1:在无重复的翻转数组中查找最小值。
当nums[mid] > nums[end] 说明mid落在第一段,最小值肯定在第二段。所以start = mid + 1。否则mid落在第二段,此时mid落在的点,也可能是最小值,所以end = mid。又因为我们在求mid的时候是向下取整,所以可以保证收敛。而收敛点即是最小点。
int findMin(vector<int>& nums) {
int start = 0;
int end = nums.size() - 1;
while (start < end) {
int mid = start + ((end - start) >> 1);
if (nums[mid] > nums[end]) {
start = mid + 1;
}
else {
end = mid;
}
}
return nums[start];
}
问题2.2 : 在有重复的翻转数组中查找最小值。
这个也是个有重复的翻转数组问题,考虑和问题1.2一样,在 nums[mid] == nums[end] 的时候,我们无法判断 mid落在哪一段,但是可以确定,nums[end] 是 <= 最小值的,这时候可以让 end--。也可以收敛到最终结果。
int findMin(vector<int>& nums) {
int start = 0;
int end = nums.size() - 1;
while (start < end) {
int mid = start + ((end - start) >> 1);
if (nums[mid] > nums[end]) {
start = mid + 1;
}
else if (nums[mid] < nums[end]) {
end = mid;
}
else {
end--;
}
}
return nums[start];
}