5 寻找旋转排序数组中的最小值(Find Minimum in Rotated Sorted Array)

1 题目

  寻找旋转排序数组中的最小值(Find Minimum in Rotated Sorted Array)

lintcode:题号——159,难度——medium

2 描述

  假设一个按升序排好序的数组在其某一未知点发生了旋转(比如0 1 2 4 5 6 7 可能变成4 5 6 7 0 1 2)。你需要找到其中最小的元素。(可以假设数组中不存在重复元素。)

  名词:

RSA:旋转排序数组,即Rotated Sorted Array

  样例1:

输入:[4, 5, 6, 7, 0, 1, 2]
输出:0
解释:

数组中的最小值为0

  样例2:

输入:[2,1]
输出:1
解释:

数组中的最小值为1

3 思路

  如果不考虑耗时,通过从头遍历的方式的时间复杂度为O(n)。考虑优化,RSA是正常的排序数组通过旋转得到的,以升序数组为例,旋转后的数组可以看作前后两段升序数组。前半段的所有值都大于后半段的所有值,需要寻找最小值,即后半段的首个元素,考虑以折半查找的方式,每次缩小一半的目标区间。

  1. 找到中点元素;
  2. 比较中点元素与末尾元素,根据情况抛掉最小值不可能存在的区间;
  3. 重复直到目标区间足够小,找到最小值。

  在第二步中,选择数组的末尾元素为参照对象进行比较,如果中点元素大于末尾元素,表明中点元素在前升序区间内,因为最小值不可能存在于前升序区间,所以抛掉前半段,折半目标区间;如果中点元素小于末尾元素,表明中点元素在后升序区间内,因为升序的原因,向右的所有元素都不会是最小值,所以可以抛掉后半段,同样能够折半目标区间。

为什么不用首位元素做为参照对象?

步骤2中以末尾元素为参照对象,如果使用首位元素为参照对象,表面上同样能够区分中点元素的位置并折半目标区间,但是考虑标准的升序数组(可以看成未旋转,或者旋转了整圈又回到远点的RSA),以末尾元素为参照,末尾元素始终大于中点元素,抛掉的一直是后半段,直到找到最小值;以首位元素为参照,首位元素始终小于中点元素,抛掉的是前半段,而最小值也被抛掉了,导致结果不正确。

技巧

选择参照对象的时候,永远使用与目标值(被寻找的最大、最小值)在同一升序、降序区间的端点。
即在升序时寻找最小值,由于最小值在后区间,我们选择末尾元素为参照对象;
在升序是寻找最大值,由于最大值在前区间,我们选择首位元素为参照对象;
在降序时寻找最小值,由于最小值在前区间,我们选择首位元素为参照对象;
在降序是寻找最大值,由于最大值在后区间,我们选择末尾元素为参照对象。

3.1 图解

中间位置元素'7',大于末尾元素'2'
中间位置元素'0',小于目标元素'2'
只剩头尾元素比较大小
RSA '4, 5, 6, 7, 0, 1, 2'
抛掉'4, 5, 6'
缩小区间至'7, 0, 1, 2'
缩小区间至'7, 0'
抛掉'1, 2'
找到目标元素'0'

3.2 时间复杂度

  算法的时间复杂度为O(log n)

3.3 空间复杂度

  算法的空间复杂度为O(1)

4 源码

  注意事项:

返回的是最小值,不是序号。

  C++版本:

/**
* @param nums: 旋转排序数组(RSA)
* @return: 数组中的最小值
*/
int findMin(vector<int> &nums) {
    // write your code here
    if (nums.empty())
    {
        return -1;
    }

    int start = 0;
    int end = nums.size() - 1;
    int mid = 0;
    while (start + 1 < end)
    {
        mid = start + (end - start) / 2;
        if (nums.at(mid) > nums.at(end))
        {
            start = mid;
        }
        if (nums.at(mid) < nums.at(end))
        {
            end = mid;
        }
    }

    if (nums.at(start) < nums.at(end))
    {
        return nums.at(start);
    }
    else
    {
        return nums.at(end);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值