题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
思路:首先理解数组的旋转定义,可以发现旋转数组可以划分为两个非减排序的子数组,而最小元素在两个子数组的分界处。
(1)我们用两个指针分别指向数组的第一个元素和最后一个元素,根据旋转数组定义,我们知道第一个元素应该是大于等于最后一个元素的。
(2)找到数组的中间元素;
如果中间元素位于前面的递增子数组,那么它应该大于等于第一个指针指向的元素,此时最小元素应该在中间元素的后面。我们把第一个指针指向中间元素。
如果中间元素位于后面的递增子数组,那么它应该小于等于第二个指针指向的元素,此时最小元素应该在中间元素的前面,我们把第二个指针指向中间元素。
可知,第一个指针始终指向前面递增子数组的元素,第二个指针总是指向后面递增子数组的元素。它们最终会相邻,而第二个指针会指向最小元素。这就是循环结束的条件。
除此,我们还需要考虑一些特殊情况:
1、数组未旋转,即旋转后的数组和原数组一致,那么第一个元素是最小元素。
2、考虑有重复元素的情况:{1, 0, 1, 1, 1}, {1, 1, 1, 0, 1}
我们无法判断中间元素属于前面的递增子数组还是属于后面的递增子数组,这时我们只能对左右指针所包围的数组进行顺序查找。
1 class Solution { 2 private: 3 int minInOrder(vector<int> v, int index1, int index2) { 4 // 找到最小元素的索引值 5 int index = index1; 6 for (int i = index1 + 1; i <= index2; i++) { 7 if (v[i] < v[index]) { 8 index = i; 9 } 10 } 11 return index; 12 } 13 public: 14 int minNumberInRotateArray(vector<int> rotateArray) { 15 int len = rotateArray.size(); 16 //如果数组长度小于等于0,返回0 17 if (len <= 0) { 18 return 0; 19 } 20 int index_l = 0; 21 int index_r = len - 1; 22 int indexMid = index_l; //一开始将指向中间元素的指针指向第一个元素,很好地解决了数组未旋转的情况 23 //确保旋转 24 while (rotateArray[index_l] >= rotateArray[index_r]) { 25 //分界点 26 if (index_r - index_l == 1) { 27 indexMid = index_r; 28 break; 29 } 30 //指向中间元素 31 indexMid = index_l + (index_r - index_l) / 2; 32 //如果index_l,indexMid, index_r指向的数字相等, 则只能顺序查找。如果index_l,index_r的指向的数字不相等, 33 //那么肯定可以确定indexMid指向的元素是属于前(或后)面递增子数组的 34 if (rotateArray[index_l] == rotateArray[index_r] && rotateArray[index_l] == rotateArray[indexMid]) { 35 indexMid = minInOrder(rotateArray, index_l, index_r); 36 break; 37 } 38 //如果中间元素大于等于第一个指针指向的元素,那么中间元素位于前面的递增子数组 39 if (rotateArray[indexMid] >= rotateArray[index_l]) { 40 index_l = indexMid; 41 } 42 //如果中间元素小于等于第二个指针指向的元素,那么中间元素位于后面的递增子数组 43 if (rotateArray[indexMid] <= rotateArray[index_r]) { 44 index_r = indexMid; 45 } 46 } 47 return rotateArray[indexMid]; 48 } 49 };