(一)题目:把一个数组最开始的若干的元素搬到数组的末尾,称这样的数组为旋转数组。例如,{3, 4, 5, 1, 2}, 最小值为1.
旋转后的数组可以看作是两个排序数组,而且前面子数组的元素都大于或等于后面子数组的元素,最小的元素恰好是两个数组的分界线。排序数组中,我们可以使用二分查找来实现O(log N) 的查找。
和二分算法一样,可以先使用两个指针,分别指向数组的第一个元素和最后一个元素。按照题目中的旋转规则,第一个元素应该大于或等于最后一个元素(数组正序的时候另加讨论)。接着,我们可以找数组中间的元素。
如果该中间元素位于前面的递增子数组,那么它应该大于等于第一个指针指向的元素。此时,数组的最小元素应该位于该中间元素的后面。可以将第一个指针指向该中间元素,这样可以缩小查找范围。移动之后前面的指针仍然位于前面的递增的子数组中。
同样,如果该中间元素位于后面的递增子数组时,它应该小于等于第二个指针指向的元素。此时,数组的最小元素应该位于该中间元素的前面(或是其本身)。可以将第二个指针指向该中间元素。移动之后后面的指针仍然位于后面的递增的子数组中。
按照上述的思路,第一个指针始终指向前一个递增的子数组,第二个指针始终指向后一个递增的子数组。最终,第一个指针将指向前一个递增子数组的最后一个元素,而第二个指针将指向后面的递增子数组的第一个元素(即数组的最小元素),也就是他们最终会指向两个相邻的元素【终止条件】。
//在旋转数组中查找最小值
#include<iostream>
using namespace std;
typedef int ElemType;
int MinInOrder(int *number, int index1, int index2)
{
int result = number[index1];
for(int i = index1 + 1; i <= index2; i++)
{
if(number[i] < result)
result = number[i];
}
return result;
}
int Min(int *number, int length)
{
if(number == NULL || length <= 0)
throw exception("Invalid input data!");
int index1 = 0;
int index2 = length - 1;
int Midindex = index1; //如果输入的数组就是一个正序的数组,即第一个元素就是最小的元素,则应直接返回第一个索引所指示的元素,因此,应将中间的索引初始化为第一个索引所指的位置
while(number[index1] >= number[index2])
{
if(index2 - index1 == 1) //当最后一个索引的位置位于第一个索引之后时,最后一个索引所指示的元素就是数组的最小值
{
Midindex = index2;
break;
}
Midindex = (index1 + index2) / 2; //中间位置索引
if(number[Midindex] >= number[index1]) //中间值大于第一个索引的值,可以判断,中间的值属于第一个递增数组
index1 = Midindex;
if(number[Midindex] <= number[index2]) //中间值小于等于最后一个索引的值,说明中间位置的值属于后半部分的第二个递增数组,且最小元素一定位于该元素之前,或者就是这个元素。
index2 = Midindex;
if(number[index1] == number[index2] && number[Midindex] == number[index1]) //第一个索引的值等于第二的索引的值等于中间索引的值,此时,无法判断中间索引的值属于前一半的数组还是后一半的数组,此时使用的是顺序查找。例如:输入的数据为:{1111101}
return MinInOrder(number, index1, index2); //返回顺序查找的结果
}
return number[Midindex];
}
int main()
{
//int arr[10] = {3, 4, 5, 1, 2};
//int arr[10] = {1, 1, 1, 1, 0, 1};
int arr[10] = {1, 2, 3, 4, 5};
int result;
result = Min(arr, 5);
cout << "The minimum is "<< result << endl;
system("pause");
return 0;
}
(二)题目:实现一个排序算法,要求其时间复杂度为O(N)。(如对公司所有员工的年龄进行排序(几万人),只允许使用常量大小的辅助空间,不得超过O(N)。)
不同的排序算法适用的场合也不尽相同。快速排序虽然总体的排序效果是最好的,但是也不是任何时候都是最优的算法。比如,数组本身已经排好序了,而每一轮排序的时候都以最后一个数字作为比较的标准,此时快速排序算法的效率只有 O(N^2)。
//对一个公司的所有员工的年龄进行排序(几万人),使用的辅助空间不得超过O(N)
#include<iostream>
using namespace std;
void AgeSort(int ages[], int length)
{
if(ages == NULL || length <= 0)
throw exception("Invalid input data!");
const int oldestAge = 99;
int timesOfAge[oldestAge + 1]; //该数组记录每一个年龄有多少人
for(int i = 0; i <= oldestAge; i++)
timesOfAge[i] = 0;
for(int j = 0; j < length; j++)
{
int age = ages[j];
if(age < 0 || age > oldestAge)
throw exception("Age out of range!");
++timesOfAge[age];
}
int index = 0;
for(int i = 0; i <= oldestAge; i++)
{
for(int j = 0; j < timesOfAge[i]; j++)
{
ages[index] = i; //年龄 i 出现了多少次,就在原数组中设置多少次i
++index;
}
}
}
int main()
{
int ages[10] = {12, 46, 35, 85, 32, 17, 95, 35, 75, 67};
AgeSort(ages, 10);
for(int i = 0; i < 10; i++)
cout << ages[i] << " ";
cout << endl;
system("pause");
return 0;
}