这是letcode上的一道题目 Leetcode: Find Minimum in Rotated Sorted Array
一、什么是旋转数组呢,就是一个有序的数组,在从某一个位置开始,旋转到数组的另一端,例如:0 1 2 3 4 5 6 -> 4 5 6 0 1 2 3
二、使用暴力的方式就是遍历这个数组,算法的复杂度就是O(N),但是根据一般的经验提到有序,就会考虑到使用二分将算法的复杂度降低到O(logn),当然这个题目也不例外,但是如何利用二分去求解呢,毕竟不是全部有序。
首先我们先找到数组的中点在慢慢的分析,设左右边界和中点分别是left,right,mid,旋转数组有个特性也是必然,那就是中点分成的两段数组必然有一段是有序的,而另一端有可能还是旋转数组或是有序的数组,例如4 5 6 0 1 2 3中点分开的两边都是有序数组,3 4 5 6 1 2中点分开的左边是有序的,右边还是旋转数组。根据这个特性我们有以下的分析。
1、如何中点分开的数组左边有序a[left] <= a[mid]
(1)同时右边也是有序的,a[mid] <= a[right],那么a[left]就是最小值
(2)如果中点到右边界是无序的 a[mid] > a[right],那么left = mid -1去右边寻找最小值
2、如果 a[left] > a[mid] 左端是无序的
(1)同时a[mid] < a[right] 那么就需要继续二分,但是不减1(因为mid有可能就是波谷)
(2)同时a[mid] > a[right] 这种情况不可能发生,因为这种情况会导致整体反序,与旋转数组不符
代码如下:
#include <stdio.h>
int find_min(int* arr, int l, int h)
{
if(arr == NULL)
return -1;
if(l > h)
return -1;
int mid;
while(l < h)
{
mid = l + (l + h) >> 1;
if(arr[l] <= arr[mid]) //左侧有序
{
if(arr[mid] <= arr[h]) //右侧也有序
{
return l;
}
else
{
l = mid + 1;
}
}
else //右侧有序 左侧有可能有波谷
{
h = mid;
}
}
return l;
}
int main(int argc, char *argv[])
{
int a1[] = {4, 5 ,6 ,1 ,2 ,3};
int a2[] = {1, 2, 3, 4, 5, 6};
int a3[] = {2, 1};
int a4[] = {1};
int a5[] = {3, 1, 2};
printf("%d\n", find_min(a1, 0, 5));
printf("%d\n", find_min(a2, 0, 5));
printf("%d\n", find_min(a3, 0, 1));
printf("%d\n", find_min(a4, 0, 0));
printf("%d\n", find_min(a5, 0, 2));
}
结果是:
3
0
1
0
1