【刷题之路】不一样的二分法

一、题目描述

原题连接: NC71 旋转数组的最小数字

描述:
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

数据范围:1 ≤ n ≤ 10000,数组中任意元素的值:0 ≤ val ≤ 10000
要求:空间复杂度:O(1),时间复杂度:O(longn)

示例1:
输入:[3,4,5,1,2]
返回值 1

示例2:
输入:[3, 100, 200, 3]
返回值 3

二、解题

1、方法1——暴力法

最简单也最通用的方法就是直接遍历整个数组找到最小值:

1.1、代码实现

int minNumberInRotateArray1(int* rotateArray, int rotateArrayLen) {
    // 特殊情况特殊处理
    if (rotateArray == NULL) {
        return -1;
    }
    if (1 == rotateArrayLen) {
        return rotateArray[0];
    }
    int min = rotateArray[0];
    int i = 0;
    for (i = 1; i < rotateArrayLen; i++) {
        if (rotateArray[i] < min) {
            min = rotateArray[i];
        }
    }
    return min;
}

时间复杂度:O(n)
空间复杂度:O(1)

2、方法2——二分法

2.1、思路解析

由题目描述可知:
旋转后的数组一定会被分成两部分:“大部分”和“小部分”,
例如:{ 4, 5, 6, 7, 1, 2, 3 } 中4567就是“大部分”,123就是“小部分”:
在这里插入图片描述
在“大部分”中最小的数也比“小部分”种最大的数要大,在“小部分”中最大的数也比“大部分”最小的数要小。
毋庸置疑,我们要找的最小值一定是在“小部分”中的,所以我们解题的关键就是要将范围缩小到“小部分”

所以我们可以看到,这个二分法和其他的二分法不太一样,这次我们需要通过mid来帮我们将范围缩小在“小部分中”,那具体怎么缩小呢?
我们可以想到,数组在经过旋转之后,right一定是指向“小部分”最大的值的,
所以我们有以下判断:
当arr[mid] > arr[right]时,就可以肯定mid的位置一定就在“大部分” 中:
在这里插入图片描述

这时候就要将范围向右缩小,执行:left = mid + 1;
当arr[mid] < arr[tight]时,就可以肯定mid的位置一定在“小部分”中:
在这里插入图片描述
这时候我们就要将范围往左缩小,执行right = mid,

这里可能有人就要有疑惑了: 为什么向右缩小范围时,可以执行left = mid + 1;而向左缩小范围时,却只能执行right = mid呢?这里的mid为什么不能减1呢?
其实这也是为了防止特殊情况的发生,就如上面的例子:
在这里插入图片描述
当mid在“大部分”中时,就算是mid已经指向了“大部分”中的最大值,那执行mid + 1跳过的也只是“大部分”中最大的之而已,对于我们找最小值没有任何影响。
但是,请看下例:
在这里插入图片描述
若是mid刚好就指向了“小部分”最小的值,那么执行mid - 1就将跳过这个最小值,right指向的就是“大部分”中的最大值了:
在这里插入图片描述
这样查找就出错了……

2.2、代码实现

有了上面的思路,那我们写代码就水到渠成了:

int minNumberInRotateArray2(int* rotateArray, int rotateArrayLen) {
	int left = 0;
	int right = rotateArrayLen - 1;
	int mid = 0;
	while (left < right) {
		mid = left + (right - left) / 2;
		if (rotateArray[mid] > rotateArray[right]) {
			left = mid + 1;
		}
		else if (rotateArray[mid] > rotateArray[right]) {
			right = mid;
		}
		else {
			right--; // 数组中可能有重复值,有重复值就要向左缩小范围
		}
	}
	return rotateArray[left];
}

时间复杂度:O(logn)
空间复杂度:O(1)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林先生-1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值