数组翻转系列

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

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

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



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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值