每日一解 旋转数组的最小数字

题目 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

示例 1:
输入:[3,4,5,1,2]
输出:1

示例 2:
输入:[2,2,2,0,1]
输出:0

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof

思路

其实就是一个查找问题,而且是一个排序数组的二分查找问题,只不过题目被这个“旋转数组”稍微的改变了一下,有了一点迷惑性。在刚看到题目的时候,自然反应是从头开始查找,O(n)的时间复杂度,找到递增数组中那个突然变小的数字即可。
但是既然是排序数组,没有更快的方法了吗?答案是有的,本题思考了一下,完全可以使用二分查找,我这里用表格演示一下:

序号 0 1 2 3 4 5 6
3 4 5 6 7 1 2

先取数组[0]的值3,二分查找数组[3]的值,值为6,大于3。那么我们就可以将范围缩小到[4] ~ [6]。然后取数组[5]的值,为1,比3小,那么将新的值设置为1,同时将范围改变到[4] ~ [5]。最后取[4]的值,为7,大于1,那么将范围确定到[5],然后输出1.
这样做的思路是这样的,因为数组是一个递增数组经过旋转后得到的,那么如果中间元素的值比开始元素偏大,那么最终答案应该在后半部分,否则在前半部分。这样时间复杂度就降低到了O(logn)。

代码实现

因为实际运行的时候遇到了这种测试用例:[10, 1, 10, 10, 10],因此在遇到middle值与answer相等的情况下,采用分治法,分别对两侧都进行二分查找就是了。结果时间结果还不如直接暴力求解的结果好,有点扼腕叹息。

class Solution {
    
    
public:
	int minArray(vector<int>& numbers) {
    
    
		int answer = numbers[0];
		if (numbers.size() == 1) {
    
    
			return answer;
		}
		int left = 0, right = numbers.size() - 1;
		answer = min(minnumber(numbers, left, right, answer), answer);
		return answer;
	}

	int minnumber(vector<int>& numbers, int left, int right, int answer) {
    
    
		while (left <= right) {
    
    
			int middle = (left + right) >> 1;
			if (numbers[middle] < answer) {
    
    
				answer = numbers[middle];
				right = middle - 1;
			}
			else if (numbers[middle] < answer) {
    
    
				left = middle + 1;
			}
			else {
    
    
				int a = min(minnumber(numbers, left, middle - 1, answer), minnumber(numbers, middle + 1, right, answer));
				if (a < answer) {
    
    
					return a;
				}
				else {
    
    
					return answer;
				}
			}
		}
		return answer;
	}
};

猜你喜欢

转载自blog.csdn.net/new_DYX/article/details/107522315