更多题目请点链接:《剑指offer》目录索引
问题描述:
把一个数组最开始的若干元素搬到数组的末尾,我们称之为数组的一个旋转,输出旋转数组的最小元素。例如,数组{3,4,5,1,2}为{1, 2, 3 ,4 , 5,}的一个旋转数组,该数组的最小值为1
分析问题:
根据描述,可以看到旋转数组有几个特点:
1) 未旋转之前的数组是一个有序数组
2) 旋转之后的数组可将其有序数组分成两部分:以最小值为界,左半部分为有序数组,右半部分有序数组
3) 数组中的第一个数字可大于或等于最后一个数字
思路:
由于旋转数组可分成两个有序数组,因此可采用二分查找的思路
1)arr[left]<=arr[mid],说明arr[mid]还在左半部分有序数组中,此时缩小范围,将left移动到mid处
2)arr[left]>arr[mid],说明left已经在左半部分数组的尾(即分界值的左边),此时在看右半部分数组,当arr[right]>=arr[mid],即arr[mid]还在右半部分数组中,此时缩小范围,将right移动到mid处
3)当left和right相邻,说明已经right在分界值处,即最小值
特殊情况:
当arr[left]=arr[right]=arr[mid]时,只能采用顺序查找,例如:旋转数组{1,0,1,1,1}
代码:
//顺序查找
int Find(int *arr,int len)
{
int min = arr[0];
int i = 0;
while(i<len)
{
if(arr[i]<min)
{
min = arr[i];
}
++i;
}
return min;
}
int ArrSearch(int *arr,int len)
{
assert(arr);
assert(len>0);
int left = 0;
int right = len-1;
int mid = 0;
if(len==1)
{
//只有一个元素
return arr[len-1];
}
while(arr[left] >= arr[right])
{
mid = left+ ((right-left)>>1);
if((right-left)==1)
{
//left和right相邻
mid = right;
break;
}
if(arr[mid] == arr[left] && arr[mid] == arr[right] && arr[left] == arr[right])
{
//特殊情况
arr[mid] = Find(arr,len);
}
if(arr[left] <= arr[mid])
{
left = mid;
}
else if(arr[right] >= arr[mid])
{
right = mid;
}
}
return arr[mid];
}
void Test()
{
int arr[]={3,4,5,1,2};
int len=sizeof(arr)/(sizeof(arr[0]));
printf("%d \n",ArrSearch(arr,len));
}
void Test1()
{
int arr[]={4,5,1,1,2,3};
int len=sizeof(arr)/sizeof(arr[0]);
printf("%d\n",ArrSearch(arr,len));
}
void Test2()
{
int arr[]={4,5,1,2,2,3,3};
int len=sizeof(arr)/sizeof(arr[0]);
printf("%d\n",ArrSearch(arr,len));
}
void Test3()
{
int arr[]={1,0,1,1,1};
int len=sizeof(arr)/sizeof(arr[0]);
printf("%d\n",ArrSearch(arr,len));
}
结果: