剑指offer---旋转数组的最小的数字

题目8:旋转数组的最小数字(leetcode链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/

问题分析

本题最直观简单的解法就是暴力法,将数组遍历一遍找出数组中的最小值返回。但是,这种做法时间复杂度较高,还可以继续优化。

旋转数组可以有以下几种情况,我们逐个分析

1)将最开始的n个数搬到数组的末尾,且n > 0 && n < numsSize(即旋转后的数组与原数组不同)

         我们注意到旋转之后的数组实际上可以划分为两个排序的子数组,而且前面的子数组的元素都大于或者等于后面子数组的元素。我们还注意到最小的元素刚好是这两个子数组的分界线。在排序的数组中我们可以用二分查找法实现O(logn)的查找。本题给出的数组在一定程度上是排序的,因此我们可以试着用二分查找法的思路来寻找这个最小的元素。
        和二分查找法一样,我们用两个指针分别指向数组的第一个元素和最后一个元素。按照题目中旋转的规则,第一个元素应该是大于或者等于最后一个元素的(这其实不完全对,还有特例,后面再加以讨论)。接着我们可以找到数组中间的元素。如果该中间元素位于前面的递增子数组,那么它应该大于或者等于第一个指针指向的元素。此时数组中最小的元素应该位于该中间元素的后面。我们可以把第一个指针指向该中间元素,这样可以缩小寻找的范围。移动之后的第一个指针仍然位于前面的递增子数组之中。
        同样,如果中间元素位于后面的递增子数组,那么它应该小于或者等于第二个指针指向的元素。此时该数组中最小的元素应该位于该中间元素的前面。我们可以把第二个指针指向该中间元素,这样也可以缩小寻找的范围。移动之后的第二个指针仍然位于后面的递增子数组之中。
       不管是移动第一个指针还是第二个指针,查找范围都会缩小到原来的一半。接下来我们再用更新之后的两个指针,重复做新一轮的查找。按照上述的思路,第一个指针总是指向前面递增数组的元素,而第二个指针总是指向后面递增数组的元素。最终第一个指针将指向前面子数组的最后一个元素,而第二个指针会指向后面子数组的第一个元素。也就是它们最终会指向两个相邻的元素,而第二个指针指向的刚好是最小的元素。这就是循环结束的条件。

2)第一种情况中,我们是把递增数组的前n个数移到数组后边,因此旋转后的数组中第一个数肯定大于等于最后一个元素。如果旋转后的数组和原数组相等,即将数组的前面的0个数移到后边,那么最后一个元素一定大于等于第一个元素。因此程序中我们还需要判断最后一个元素是否会大于最后一个元素,如果是我们就直接返回第一个元素。

3)基于上面两种情况,我们还需要考虑,如果数组元素全部相等(begin mid end的值相等)就无法使用二分查找判断,这是我们就需要遍历数组查找。

代码描述

int Min(int* nums, int numsSize)
{
	//定义三个指针
	int begin = 0, end = numsSize - 1;
	int mid = begin;//这里让mid = begin,防止数组有序需要返回第一个元素

	//如果数组元素有序(最后一个元素大于第一个元素),就直接返回第一个元素
	while (nums[begin] >= nums[end])
	{
		//如果两个指针相等,就可以返回后边指针的值了
		if (end - begin == 1)
		{
			mid = end; //要保证,数组元素有序返回mid值,因此要让mid == end
			break;
		}
		mid = (begin+end) / 2;
		//如果三个数相等,只能顺序查找
		if (nums[begin] == nums[end] && nums[begin] == nums[mid])
		{
			int result = nums[begin];
			for (int i = 0; i <= end; i++)
			{
				if (result > nums[i])
					return nums[i];
			}
			//如果数组中的所有元素都相等,则result不可能大于nums[i],这时我们返回数组中的最后一个元素即可
			return nums[end];
		}
		//判断最小元素在数组中的位置
		else if (nums[begin] <= nums[mid])
			begin = mid;
		else if (nums[end] >= nums[mid])
			end = mid;
	}
	return nums[mid];
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

疯狂嘚程序猿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值