问答题 3.  把一个有序数组最开始的若干个元素搬到数组的末尾,称之为数组的旋转。输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。例如数组[3,4,5,1,2]为数组[1,2,3,4,5]的一个旋转,该数组的最小值为1。
【正确答案】Python中可以使用列表来表示有序数组,因此示例中都用列表来表示有序数组。
   其实这是一个非常基本和常用的数组操作,它的描述如下:
   有一个数组X[0...n-1],现在把它分为两个子数组:x1[0...m]和x2[m+1...n-1],交换这两个子数组,使数组x由x1x2变成x2x1,例如x=[1,2,3,4,5,6,7,8,9],x1=[1,2,3,4,5], x2=[6,7,8,9],交换后,x=[6,7,8,9,1,2,3,4,5]。
   对于本题的解决方案,最容易想到的,也是最简单的方法就是直接遍历法。但是这种方法显然没有用到题目中旋转数组的特性,因此,它的效率比较低下,下面介绍一种比较高效的二分查找法。
   通过数组的特性可以发现,数组元素首先是递增的,然后突然下降到最小值,然后再递增。虽然如此,但是还有下面三种特殊情况需要注意:
   (1)数组本身是没有发生过旋转的,是一个有序的数组,例如序列[1,2,3,4,5,6]。
   (2)数组中元素值全部相等,例如序列[1,1,1,1,1,1]。
   (3)数组中元素值大部分都相等,例如序列[1,0,1,1,1,1]。
   通过旋转数组的定义可知,经过旋转之后的数组实际上可以划分为两个有序的子数组,前面的子数组的元素值都大于或者等于后面子数组的元素值。可以根据数组元素的这个特点,采用二分查找的思想不断缩小查找范围,最终找出问题的解决方案,具体实现思路如下所示:
   按照二分查找的思想,给定数组arr,首先定义两个变量low和high,分别表示数组的第一个元素和最后一个元素的下标。按照题目中对旋转规则的定义,第一个元素应该是大于或者等于最后一个元素的(当旋转个数为0,即没有旋转的时候,要单独处理,直接返回数组第一个元素)。接着遍历数组中间的元素arr[mid],其中mid=(high+low)/2。
   (1)如果arr[mid]<arr[mid-1],则arr[mid]一定是最小值;
   (2)如果arr[mid+1]<arr[mid],则arr[mid+1]一定是最小值;
   (3)如果arr[high]>arr[mid],则最小值一定在数组左半部分;
   (4)如果arr[mid]>arr[low],则最小值一定在数组右半部分;
   (5)如果arr[low]==arr[mid]且arr[high]==arr[mid],则此时无法区分最小值是在数组的左半部分还是右半部分(例如:[2,2,2,2,1,2],[2,1,2,2,2,2,2])。在这种情况下,只能分别在数组的左右两部分找最小值minL与minR,最后求出minL与minR的最小值。
   示例代码如下:
   #python中可以使用列表来表示有序数组, 因此示例代码中使用列表来表示有序数组。
   def getMin_1(arr,low,high):
   #如果旋转个数为0, 即没有旋转, 单独处理, 直接返回数组头元素
   if high<low:
   return arr[0]
   #只剩下一个元素一定是最小值
   if high==low:
   return arr[low]
   #mid=(low+high)/2, 采用下面写法防止溢出
   mid=low+((high-low)>>1)
   #判断是否arr[mid]为最小值
   if arr[mid]<arr[mid-1]:
   return arr[mid]
   #判断是否arr[mid+1]为最小值
   elif arr[mid+1]<arr[mid]:
   return arr[mid+1]
   #最小值一定在数组左半部分
   elif arr[high]>arr[mid]:
   return getMin(arr,low,mid-1)
   #最小值一定在数组右半部分
   elif arr[mid]>arr[low]:
   return getMin(arr, mid+1, high)
   #arr[low]==arr[mid] && arr[high]=arr[mid]
   #这种情况下无法确定最小值所在的位置, 需要在左右两部分分别进行查找
   else:
   return min(getMin(arr,low, mid-1), getMin(arr, mid+1,high)
   
   def getMin(arr):
   if None==arr:
   print "参数不合法"
   return
   else:
   return getMin_1(arr,0,len(arr)-1)
   
   if __name__=="__main__":
   array1=[5,6,1,2,3,4]
   mins=getMin(arrayl)
   print mins
   array2=[1,1,0,1]
   mins=getMin(array2)
   print mins
   程序的运行结果为:
   1
   0
   算法性能分析:
   一般而言,二分查找的时间复杂度为O(log2N),对于这道题而言,大部分情况下时间复杂度为O(log2N),只有每次都满足第(5)条的时候才需要对数组中所有元素都进行遍历,因此,这种方法在最坏的情况下的时间复杂度为O(N)。
【答案解析】

[考点] 如何找出旋转数组的最小元素。