剑指offer7:旋转数组中的最小数字

内容:

      把一个数组最开始的若干个元素搬到数组末尾,我们称之为数组的旋转。输入一个递增序列的数组的一个旋转,输出旋转数组的最小元素。如:{3,4,5,1,2}是{1,2,3,4,5}的一个旋转数组。最小值为1.

解题思路:

如果从头到尾遍历数组,能找到这个元素,但是时间复杂度为O(n),对于旋转数组来说不可取。

我们注意到数组旋转之后实际上可以划分两个排序的子数组,而且前面的数组元素都大于或等于后面的数组的元素,最小的元素刚好是这两个子数组的分界线。和二分法一样分别用两个指针指向数组的第一个元素和最后一个,第一个元素大于或等于最后一个元素(除特例外),接着找到中间元素,如果中间元素位于前面的递增数组,此时最小值一定在后面的子数组中,把第一个指针移到中间值上,如果移动后的第一个指针仍然位于前一个子数组,则继续。如果中间数位于第二个子数组,那么中间数一定小于或者等于最后一个元素,此时最小元素应该位于中间数前面,此时移动第二个指针到中间数,。依次重复上述步骤。

代码实现:

int is_min(int *a,int length)
{
	assert(a);
	assert(length > 0);
	int left = 0;
	int right = length - 1;
	int mid = 0;
	while (left <= right){
		if (right - left == 1){
			mid = right;
			break;
		}
		mid = ((right - left) >> 1) + left;
		if (a[mid] > a[left])
			left = mid;
		else if (a[mid] < a[right])
			right = mid;
	}
	return a[mid];
}

特殊情况:如果数组为:{1,0,1,1,1},中间数为1,无法确定中间数属于前一个子数组递增,还是后一个子数组递增,所以就无法用指针来移动来缩小范围查找。所以需要用顺序排序来确定:

代码实现:

int Min(int *a ,int left, int right)
{
	int ret = a[left];
	int i = left + 1;
	while (i<right){
		if (ret>a[i])
			return a[i];
		i++;
	}
	return ret;
}
int is_min(int *a,int length)
{
	assert(a);
	assert(length > 0);
	int left = 0;
	int right = length - 1;
	int mid = 0;
	while (left <= right){
		if (right - left == 1){
			mid = right;
			break;
		}
		mid = ((right - left) >> 1) + left;
		if ((a[left] == a[mid]) && (a[right] == a[mid]))       //特殊情况,当中间值,第一个元素,最后一个元素都相等时
			return Min(a,left,right);
		else if (a[mid] > a[left])     //中间值在前一部分
			left = mid;
		else if (a[mid] < a[right])    //中间值在后一部分
			right = mid;
	}
	return a[mid];
}

猜你喜欢

转载自blog.csdn.net/qq_41889292/article/details/80484657