154. 寻找旋转排序数组中的最小值 II AND 540. 有序数组中的单一元素

154. 寻找旋转排序数组中的最小值 II

有两种解法,第一种是暴力法,依次比较遇到的元素和下一个元素,但这样在最坏的情况下会浪费时间(最小的元素在最后),时间复杂度会超。

另一种解法是采用二分法。

对于能够使用二分法的题,一般来说会根据下面两点来采用:

  • 顺序存储(能够通过下标获取关键字)
  • 元素有序(能够通过任意关键字的值来确定所找关键字的位置)

虽然此题在表面上没有满足元素有序,但是实际上还是能够通过任意关键字的值来确定所找关键字的位置,原因在于旋转的是一部分,这也就能够保证部分有序。利用二分法进行nums[mid]与nums[right]的比较,从而不断缩小查找范围,会有三种情况:

  • nums[mid] < nums[right]。此时,最小值一定在左侧,且nums[mid]有可能是最小值,故在这种情况下,缩小范围时,要让right = mid,而不是right = mid - 1。
  • nums[mid] > nums[right]。此时,最小值一定在右侧,且nums[mid]不可能是最小值,故在这种情况下,缩小范围时,left = mid + 1。
  • nums[mid] == nums[right]。此时说明数组中有重复的元素,这时需要用一种方法来缩小范围:暴力法,也就是令right--。这样并不会影响结果,原因在于存在与nums[right]相同的值,不需要担心会漏掉最小值。

代码如下:

int findMin(int* nums, int numsSize){
    int left,right,mid;
    left = 0; right = numsSize - 1;
    while(left < right){
        mid = left + (right - left) / 2;
        if(nums[mid] > nums[right])//条件满足则说明最小值一定在右侧,且nums[mid]一定不是最小值
            left = mid + 1;
        else 
            if(nums[mid] < nums[right])//条件满足则说明最小值一定在左侧,且nums[mid]有可能是最小值
                right = mid;           //这也就是为什么不是right = mid - 1的原因
            else//数字中存在相同的元素
                right--;
    }
    return nums[left];
}

540. 有序数组中的单一元素

 同样也是采用二分法,在这之前,必须搞懂如何二分及二分的规则:

由题目可以知道,数组有序且仅有一个元素只会出现一次,其它元素均会出现两次,我们记这个只出现一次的元素为x,观察下标可以得到如下规则:

  • 假设不存在x,则下标为偶数的元素(设下标为mid)一定与下标为mid+1的元素相等,下标为奇数的元素(设为mid)一定与下标为mid-1的元素相等,我们记这样的规则为初始规则
  • 而有了x之后,x左侧的元素仍然满足此规则,而x右侧的元素满足相反的规则,我们记这样的规则为变化规则

由此,可以写出代码:

int singleNonDuplicate(int* nums, int numsSize){
    int left,right,mid;
    left = 0; right = numsSize - 1;
    while(left < right){
        mid = left + (right - left) / 2;
        if(mid % 2 == 0){//当下标为偶数时
            if(nums[mid] == nums[mid + 1])//若初始规则成立,说明单元素在mid右侧,并未影响左侧元素的初始规则
                left = mid + 1;
            else//如果规则不成立,说明是因为有单元素的插入导致的规则改变,且nums[mid]也有可能是单元素
                right = mid;
        }
        else{//奇数下标的情况相同
            if(nums[mid] == nums[mid - 1])
                left = mid + 1;
            else    
                right = mid;
        }
    } 
    return nums[left];
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值