1、有序数组旋转后寻找数组中的最小值
一般情况下我们的第一反应肯定是遍历此数组,但这肯定不是我们所期望的,因为此数组是有序数组旋转而成,所以我们此时应该想到用二分法来寻找数组中的最小值。假设我们给定递增数组{0,1,2,3,4,5,6,7,8,9}
旋转后的数组有下面几种形式
{6,7,8,9,0,1,2,3,4,5}
{9,0,1,2,3,4,5,6,7,8}
{0,1,2,3,4,5,6,7,8,9}
{5,6,7,8,9,0,1,2,3,4}
通过观察可以发现,在经过旋转后的数组在寻找最小值的过程中0的前面的数总是大于0后面的数,所以我们可以以此为条件,在将范围缩小为2个数时,后面的数就是最小值,根据此思路代码如下:
<span style="font-family:Microsoft YaHei;font-size:14px;">int MinArray(int* a, int n)
{
assert(a != NULL || n <= 0);
int start = 0;
int end = n - 1;
int mid = start;
while (a[start] >= a[end]) //确定数组经过旋转已经不是有序数组
{
if (end - start == 1) //当只剩两个数时直接返回较小值的下标
{
mid = end;
break;
}
mid = (start + end) / 2;
//如果a[start],a[mid],a[end]相等时,只能遍历寻找最小值
if (a[mid] == a[start] && a[mid] == a[end])
{
int min = a[start];
while (start <= end)
{
if (a[start] < min)
{
mid = a[start];
}
++start;
}
return min;
}
if (a[mid] >= a[start])
{
start = mid;
}
else if (a[mid] <= a[end])
{
end = mid;
}
}
return a[mid];
}</span>
上面的程序已经将一种特殊情况给处理了,例如下面这种例子
{1,0,1,1,1}
{1,1,1,0,1}
此时若直接进行比较,将会出现将最小值跳过的情况,所以我们在此种情况下只能采取遍历寻找最小值。
我们也可以使用下标的方式来实现寻找最小值,思路和上面的程序一样,代码如下:
上面是求去旋转有序数组的最小值,求最大值的思路和最小值的一样,只需要在循环结束返回时返回那个较大值即可。
<span style="font-family:Microsoft YaHei;font-size:14px;">int MinArray2(int* a, int n)
{
assert(a != NULL || n <= 0);
int start = 0;
int end = n - 1;
while (start < (start+end)/2)
{
int mid = (start + end) / 2;
//如果a[start],a[mid],a[end]相等时,只能遍历寻找最小值
if (a[mid] == a[start] && a[mid] == a[end])
{
int min = a[start];
while (start <= end)
{
if (a[start] < min)
{
mid = a[start];
}
++start;
}
return min;
}
if (a[mid] >= a[start] && a[mid] <= a[end]) //此时数组有序,直接返回数组下标0
{
return 0;
}
if (a[mid] >= a[start])
{
start = mid;
}
else if (a[mid] <= a[end])
{
end = mid;
}
}
if (a[start] > a[end])
{
return a[end];
}
return a[start];
}</span>
上面是求去旋转有序数组的最小值,求最大值的思路和最小值的一样,只需要在循环结束返回时返回那个较大值即可。
2、有序数组旋转后给定数值返回此数值在数组中的下标
实现在一个旋转有序数组中寻找一个数并返回下标,很多人的第一反应肯定是直接遍历,但这样的话有序数组旋转的特性没有得到应用,所以我们需要分析旋转有序数组的特点,例如下面旋转有序数组
{5,6,7,8,9,0,1,2,3,4}
{6,7,8,9,0,1,2,3,4,5}
{0,1,2,3,4,5,,6,7,8,9}
通过观察上面的数组我们可以发现无论数组如何旋转,数组分成两个子数组时,一个肯定是有序的,所以我们可以利用此特性来实现寻找一个特定的数,每次将给定数值通过和有序数组的首尾两个值对比,来判断此数是否存在在这个数组中,如果在的话,将此数组递归,若不在,将另一个数组递归,重复此过程,直至首尾元素下标相同,或寻找到此给定数值。
依据上面的思路实现的代码如下:
<span style="font-family:Microsoft YaHei;font-size:14px;">int Search(int* a, int n, int x)
{
assert(a != NULL || x <= 0);
int start = 0;
int end = n - 1;
while (start < end)
{
int mid = (start + end) / 2;
if (a[mid] == x)
{
return mid;
}
else if (a[mid] > a[start])
{
if (x <= a[mid] && x >= a[start])
{
end = mid;
}
else
{
start = mid;
}
}
else if (a[end] > a[mid])
{
if (x >= a[mid] && x <= a[end])
{
start = mid;
}
else
{
end = mid;
}
}
else
{
++start; //处理数组中有重复的数字
}
}
return -1;
}</span>