循环有序数组:

把一个有序数组从某个(未知)位置处截为两段,把前一段放到后一段的后面(数组元素两部分分别有序)
采用二分查找,时间复杂度要求O(log n)

主要难点:

二分查找时左右边界的确定
确定分割点之后,前半段和后半段中必然有一部分有序:

1
2
3
4
5
6
7
8
if(nums[first] <= nums[mid]) //前半部有序
{
//前半段有序时,判定target是否在前半段
}
else //后半部有序
{
//后半段有序时,判定target是否在后半段
}

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public:
int search(vector<int>& nums, int target) {
int first = 0;
int last = nums.size();

while (first != last) {
const int mid = first + (last - first) / 2;
if (nums[mid] == target) return mid;

if (nums[first] <= nums[mid]) {
if (nums[first] <= target && target < nums[mid]) { // 改进:将target与first的判等独立出来
last = mid;
} else {
first = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[last - 1]) { // 改进:将target与last-1的判等独立出来
first = mid + 1;
} else {
last = mid;
}
}
}

return -1;
}
};

若数组中允许存在重复元素:

nums[first] <= nums[mid]时,判断前半段有序的假设不再成立。如[2,3,4,2,2,2,2]。

解决:把条件拆分开

1
2
3
4
5
6
7
if (nums[first] < nums[mid]) {
// 确定前半段有序
} else (nums[first] > nums[mid]) {
// 确定后半段有序
} else {
// ++first,去除一个重复
}

Comments

2016-03-04