把一个数组最开始的若干个元素搬到数组的末尾,我们称为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组中最小的元素。例如:数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小元素为 1 。
直接反应出来的是把所有数遍历一边,找出最小的数。但本题一定有特殊性,因为分成了两个递增的子数组。那么,为何不利用头尾指针采取两头往中间的临界值遍历,这种方法大大降低了时间复杂度。这种方法类似二分法。
#include <iostream>
using namespace std;
int MyMin(int a[],int len)
{
int Head = 0, Tail = len-1,Middle = Head;//头尾遍历数组的下标 先把中间元素下标置0
// 同样,中间元素 小于 尾元素,那么 中间元素 在 右数组中,即目标最小的数还在中间元素的前面
while(a[Head]>=a[Tail])// 当首指针指向的元素 大于 尾指针指向的元素时,如果 首位指针挨在一起,那么尾指针所指向的一定是最小元素
{
if(Tail-Head==1)
{
Middle = Tail ;
break;
}
// 当 头尾指针 不相邻时,取中间指针,并将 中间指针 所指向的元素 与 头指针 和 尾指针 指向的元素的 比较大小
Middle = (Head+Tail)/2;
// 如果 中间元素 大于 头元素,那么 中间元素 还在 左数组中,即目标最小的数还在右数组中,需要逐渐缩小 范围,于是 就将头指针 指向中间元素
if(a[Middle]>=a[Head])
Head = Middle;
// 同样,中间元素 小于 尾元素,那么 中间元素 在 右数组中,即目标最小的数还在中间元素的前面
else if(a[Middle]<=a[Tail])
Tail = Middle;
}
return Middle;//返回最小值下标
}
int main()
{
//定义一个旋转数组
int a[] = { 4, 5, 6, 7, 0, 1, 2, 3 };
int length = sizeof(a) / sizeof(a[0]);
int min = MyMin(a, length);
cout << "Min:" << a[min] << endl;
return 0;
}
与此同时,我们会忽略程序的特殊性。如果一个数组1, 0,1,1,1 旋转之后1,1,1,0,1。这个时候头和尾指向的元素一样,你还能用中间元素去判别吗?显然不行。遇到这种情况下,只能顺序遍历了。
#include <iostream>
using namespace std;
int MinOrder(int a[],int Head,int Tail)
{
int result = a[Head];
for (int i = Head + 1; i <= Tail; i++)
{
if (a[i] < result)
result = a[i];
}
return result;
}
int MyMin(int a[], int len)
{
int Head = 0, Tail = len - 1, Middle = Head;
while (a[Head] >= a[Tail])
{
if (Tail - Head == 1)
{
Middle = Tail;
break;
}
Middle = (Head + Tail) / 2;
//判断分类操作 当出现 数组首元素 和 尾元素 都等于 中间元素时,调用另一个函数
if (a[Middle] == a[Head] && a[Head] == a[Tail])
return MinOrder(a, Head, Tail);
if (a[Middle] >= a[Head])
Head = Middle;
else if (a[Middle] <= a[Tail])
Tail = Middle;
}
return a[Middle];
}
int main()
{
// 定义一个旋转数组
int a[] = { 1, 1, 1, 1, 0, 1 };
int length = sizeof(a) / sizeof(a[0]);
int min = MyMin(a, length);
cout << "Min:" << min << endl;
return 0;
}