内容:
把一个数组最开始的若干个元素搬到数组末尾,我们称之为数组的旋转。输入一个递增序列的数组的一个旋转,输出旋转数组的最小元素。如:{3,4,5,1,2}是{1,2,3,4,5}的一个旋转数组。最小值为1.
解题思路:
如果从头到尾遍历数组,能找到这个元素,但是时间复杂度为O(n),对于旋转数组来说不可取。
我们注意到数组旋转之后实际上可以划分两个排序的子数组,而且前面的数组元素都大于或等于后面的数组的元素,最小的元素刚好是这两个子数组的分界线。和二分法一样分别用两个指针指向数组的第一个元素和最后一个,第一个元素大于或等于最后一个元素(除特例外),接着找到中间元素,如果中间元素位于前面的递增数组,此时最小值一定在后面的子数组中,把第一个指针移到中间值上,如果移动后的第一个指针仍然位于前一个子数组,则继续。如果中间数位于第二个子数组,那么中间数一定小于或者等于最后一个元素,此时最小元素应该位于中间数前面,此时移动第二个指针到中间数,。依次重复上述步骤。
代码实现:
int is_min(int *a,int length) { assert(a); assert(length > 0); int left = 0; int right = length - 1; int mid = 0; while (left <= right){ if (right - left == 1){ mid = right; break; } mid = ((right - left) >> 1) + left; if (a[mid] > a[left]) left = mid; else if (a[mid] < a[right]) right = mid; } return a[mid]; }
特殊情况:如果数组为:{1,0,1,1,1},中间数为1,无法确定中间数属于前一个子数组递增,还是后一个子数组递增,所以就无法用指针来移动来缩小范围查找。所以需要用顺序排序来确定:
代码实现:
int Min(int *a ,int left, int right) { int ret = a[left]; int i = left + 1; while (i<right){ if (ret>a[i]) return a[i]; i++; } return ret; } int is_min(int *a,int length) { assert(a); assert(length > 0); int left = 0; int right = length - 1; int mid = 0; while (left <= right){ if (right - left == 1){ mid = right; break; } mid = ((right - left) >> 1) + left; if ((a[left] == a[mid]) && (a[right] == a[mid])) //特殊情况,当中间值,第一个元素,最后一个元素都相等时 return Min(a,left,right); else if (a[mid] > a[left]) //中间值在前一部分 left = mid; else if (a[mid] < a[right]) //中间值在后一部分 right = mid; } return a[mid]; }