此部分关于在旋转数组中搜索的题目较多,因此需要扩展上一篇中二分查找算法的基础框架。补充:这种在array中进行搜索的题目都最好进行边界检查。
Find Minimum in Rotated Sorted Array I
将一个有序的数组在中间某个地方进行旋转可以看成如图所示。此题为寻找数组中的最小值,即为图中红色圆圈部分。
题目可以复用二分查找的基本框架,关键在与target的选择,我们需设定target = nums[nums.length - 1],这样题目可转化为:寻找第一个比目标数小的位置。
Find Minimum in Rotated Sorted Array II
此题目与Find Minimum in Rotated Sorted Array I的不同之处仅在于数组中存在重复元素(duplicates),如此也可使用上道题目的解题思路和二分查找法的基础框架,需要注意的代码部分:
while (start + 1 < end) {
int mid = start + (end - start) / 2;
if (nums[mid] == nums[end]) {
end--;
} else if (nums[mid] < nums[end]) {
end = mid;
} else {
start = mid;
}
}
上述代码的”<=”可合并到一起,因为按上一篇blog所讲,此代码框架为偏向左边的,即偏向前,不用担心会被”卡住”。
但此道题目需要注意的并不是可以用二分查找来做,而是当数组中存在重复元素时,算法效率并不是O(lgn),而是O(n),极端的例子为在”1111111…”存在一个”0”。此时使用一个for循环进行顺序查找即可解决,并具有相同的时间复杂度。
Search in Rotated Sorted Array I
题目为搜索旋转数组中的目标数的位置,若不存在则返回-1。可如图所示,目标数为红色圆圈,题目的难点在于当与目标数进行比较时,大于目标数的部分有两段(蓝色),所以我们并不知道nums[mid]是在target的左边还是右边,这样就无法移动start和end了。
我们可以分两种情况来看,如图所示,绿色部分和蓝色部分。此时,我们使用A[start] 与 A[mid]进行比较即可区分开是在哪一部分,然后通过比较A[start]、target、A[end]和A[mid]的位置即可进行移动操作了。
代码:
while (start + 1 < end) {
mid = start + (end - start) / 2;
if (A[mid] == target) {
return mid;
}
if (A[start] < A[mid]) {
// 绿色部分
if (A[start] <= target && target <= A[mid]) {
end = mid;
} else {
start = mid;
}
} else {
// 蓝色部分
if (A[mid] <= target && target <= A[end]) {
start = mid;
} else {
end = mid;
}
}
}
Search in Rotated Sorted Array II
这道题与Search in Rotated Sorted Array I的区别也是仅在于数组中存在重复元素,因此与Find Minimum in Rotated Sorted Array II极为类似,时间复杂度为O(n),使用一个顺序查找即可解决,这里不再赘述。
Recover Rotated Sorted Array
最后一道题为恢复旋转数组,与搜索无关。举个例子:67812345,我们可以先找到8的位置然后将6到8进行逆转,再将8后面的1到5进行逆转,如此可得到:87654321,最后进行一次所有元素的逆转,即可得到12345678,恢复完毕。代码:
private void reverse(ArrayList<Integer> nums, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
int temp = nums.get(i);
nums.set(i, nums.get(j));
nums.set(j, temp);
}
}
public void recoverRotatedSortedArray(ArrayList<Integer> nums) {
for (int index = 0; index < nums.size() - 1; index++) {
if (nums.get(index) > nums.get(index + 1)) {
reverse(nums, 0, index);
reverse(nums, index + 1, nums.size() - 1);
reverse(nums, 0, nums.size() - 1);
return;
}
}
}
Rotate String
此题目放在这是因为它与Recover Rotated Sorted Array解题思路很相似,根据偏移量将字符串进行旋转。代码为:
public char[] rotateString(char[] A, int offset) {
if (A == null || A.length == 0) {
return A;
}
offset = offset % A.length;
reverse(A, 0, A.length - offset - 1);
reverse(A, A.length - offset, A.length - 1);
reverse(A, 0, A.length - 1);
return A;
}
private void reverse(char[] A, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
char temp = A[i];
A[i] = A[j];
A[j] = temp;
}
}