数组翻转系列

来源:互联网 发布:大腿内侧黑色素知乎 编辑:程序博客网 时间:2024/06/16 20:15

数组翻转后形成的数组,如何在这个数组中查找某个数?如何查找最小值?

查找有序数组当然是二分数组最快了。但是现在这个有序数组经过了翻转,形成了两端有序数组。怎么办?

最好的思想,是把图画出来,再进行分析:



原始数组是一个递增(或非递减)数组,翻转数组则如右图所示。我们可以利用 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];    }


原创粉丝点击