剑指offer面试题(11)——旋转数组的最小数字
程序员文章站
2022-06-17 19:54:00
...
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转,该数组的最小值为 1 。
解题思路
最直观的做法是把旋转数组从前到后遍历一遍,其时间复杂度为 O(n)。很明显,这种解法效率较低。
主要代码如下:
int FindMinElement(int* array,int length)
{
int minElement = array[0];
if (array == nullptr || length <= 0)
throw new std::exception("Invalid parameters");
for (int i = 0; i < length; ++i)
{
if (array[i] <= minElement)
minElement = array[i];
}
return minElement;
}
旋转后的数组实际为两个排序子数组的组合,在排序的数组中可以用二分查找的方法来查找最小数字,其时间复杂度为O(logn)。每次查找都把旋转数组平均分成两部分,通过比较当前旋转数组两端点和中间点的值,判断最小值在数组的哪一部分,从而达到缩小搜索范围的目的。具体过程如下图所示:
需要注意的是,当旋转数组的两端点的值都与中间点的值相等时,无法判断最小值在哪一部分,因此需要采用顺序查找方法,其查找过程如下图所示:
上述查找过程主要实现代码如下:
int MinInOrder(int* array,const int& index1, const int& index2)
{
int minNumber = array[index1];
for (int i = index1 + 1; i <= index2; ++i)
{
if (array[i] < minNumber)
minNumber = array[i];
}
return minNumber;
}
int FindMinElement(int* array, const int& length)
{
//输入合法检查
if (array == nullptr || length <= 0)
throw new std::exception("Invalid parameters");
int index1 = 0;
int index2 = length-1;
int midIndex = 0;
//如果旋转数组前面的0个数字,则直接跳过,返回第一个元素
while (array[index1] >= array[index2])
{
if (index2 - index1 == 1)
{
midIndex = index2;
break;
}
midIndex = (index1 + index2) / 2;
//如果index1,index2,midIndex三者指向的元素大小相等,
//则只能顺序查找最小元素
if (array[midIndex] == array[index1] && array[midIndex] <= array[index2])
return MinInOrder(array,index1, index2);
if (array[midIndex] >= array[index1])
index1 = midIndex;
else if(array[midIndex] <= array[index2])
index2 = midIndex;
}
return array[midIndex];
}
当题目要求在排序的数组或部分排序的数组中查找某一个数字或统计某个数字出现的次数时,都可以尝试使用二分查找方法。测试用例1
// ====================测试代码====================
void Test(int* array, int length, int expected)
{
int result = 0;
try
{
result = FindMinElement(array, length);
for (int i = 0; i < length; ++i)
cout << array[i] << endl;
if (result == expected)
cout << "\tpassed\n" << endl;
else
cout << "\tfailed\n" << endl;
}
catch (...)
{
if (array == nullptr)
cout << "Test passed.\n" << endl;
else
cout << "Test failed.\n" << endl;
}
}
int main()
{
// 典型输入,单调升序的数组的一个旋转
int array1[] = { 3, 4, 5, 1, 2 };
Test(array1, sizeof(array1) / sizeof(int), 1);
// 有重复数字,并且重复的数字刚好的最小的数字
int array2[] = { 3, 4, 5, 1, 1, 2 };
Test(array2, sizeof(array2) / sizeof(int), 1);
// 有重复数字,但重复的数字不是第一个数字和最后一个数字
int array3[] = { 3, 4, 5, 1, 2, 2 };
Test(array3, sizeof(array3) / sizeof(int), 1);
// 有重复的数字,并且重复的数字刚好是第一个数字和最后一个数字
int array4[] = { 1, 0, 1, 1, 1 };
Test(array4, sizeof(array4) / sizeof(int), 0);
// 单调升序数组,旋转0个元素,也就是单调升序数组本身
int array5[] = { 1, 2, 3, 4, 5 };
Test(array5, sizeof(array5) / sizeof(int), 1);
// 数组中只有一个数字
int array6[] = { 2 };
Test(array6, sizeof(array6) / sizeof(int), 2);
// 输入nullptr
Test(nullptr, 0, 0);
system("pause");
return 0;
}
推荐阅读
-
【剑指 Offer-python】 03. 数组中重复的数字
-
【剑指Offer】最小的K个数:[数组][高级算法]
-
剑指offer 56 数组中数字出现的次数 lintcode 82. 落单的数、83. 落单的数 II、84. 落单的数 III
-
【剑指offer】面试题56(1):数组中只出现一次的两个数字
-
剑指offer:数组中只出现一次的两个数字(java版)
-
剑指offer 面试题56 python版+解析:数组中只出现一次的两个数字,数组中唯一只出现一次的数字
-
剑指offer第二版-56.数组中只出现一次的两个数字
-
【算法分享】剑指offer56-数组中只出现一次的两个数字
-
剑指 Offer 56 - I. 数组中只出现一次的两个数字
-
《剑指Offer》Java刷题 NO.40 数组中只出现一次的数字(数组、HashMap、位运算、异或)