剑指Offer第6题:旋转数组的最小数字

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

此题与leecode 153类似,不同之处在于leecode-153是严格递增的数组,而此题是单调不减的数组。注意,在leecode的测试用例中,存在不旋转数组的测试用例。

解题思路:最直接的想法是,遍历一遍数组,找到最小值,然而这种算法的时间复杂度为*O(n)*,没有利用到给出的旋转性质。第二种方法就是遍历数组,找到旋转点的位置,即Array[i] > Array[i+1],则Array[i+1]就是我们要找的最小值,这种算法没有利用数组是排序过的性质。对于排序过的数组,自然而然想到二分查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
int minNumberInRotateArray(vector<int> rotateArray) {
if (rotateArray.size()==0) return 0;
int left = 0;
int right = rotateArray.size()-1;
int mid = 0;
while (left < right) {
mid = (left+right) / 2;
if (rotateArray[mid] == rotateArray[right]) right -= 1; //存在相等的值,去掉最右边的一位
else if (rotateArray[mid] < rotateArray[right]) right = mid; //右边的是正常递增的,所以断点在左边
else left = mid + 1; // 断点在右边,但是此时 rotateArray[mid] > rotateArray[right], 所以mid处的值可以抛弃,而上一中情况中mid处的值是小于其他值的,不可直接删除。
}
return rotateArray[left];
}

注意,下面这种写法无法通过测试用例 {5,5,5,5,5,1,2,5},会陷入死循环中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int minNumberInRotateArray(vector<int> rotateArray) {
if (rotateArray.size()==0) return 0;
int left = 0;
int right = rotateArray.size()-1;
int mid = 0;
if (rotateArray[left] < rotateArray[right]) return rotateArray[0];
while (left < right) {
mid = (left+right) / 2;
if (rotateArray[left] < rotateArray[right]) return rotateArray[left];
if (rotateArray[mid] > rotateArray[mid+1]) return rotateArray[mid+1];
if (rotateArray[mid] < rotateArray[left]) right = mid;
if (rotateArray[mid] > rotateArray[right]) left = mid;
}
}