题解|旋转数组中的二分查找
描述
输入一个递增排序的数组(元素不重复)的一个旋转(次数不详),找出某个元素.
输入
第一行:N,数组的长度
第二行:N个整数,作为数组的元素,空格分开
第三行:要查找的关键字K
输出
关键字K的下标,如果没有找到,输出-1
样例输入
5
6 1 2 3 4
1
样例输出
1
思路
O(n)的算法不写了,下面是O(lgn)的。
在旋转数组中先找到最小的一个元素位置,例如[7,9,1,3,5]中的1。先判断目标数字是否等于最小数字,再比较目标数字k与arr[end]的大小,确定应该在最小数字的左侧进行二分查找还是右侧进行二分查找。
import java.util.Scanner;
public class FindInRotaryArr {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int ans = -1;
int n = sc.nextInt();
int[] arr = new int[n];
for (int i = 0; i < arr.length; i++) {
arr[i] = sc.nextInt();
}
int k = sc.nextInt();
int indexOfMin = f(arr);
if (k == arr[indexOfMin]) {
ans = indexOfMin;
}
int end = arr.length - 1;
if (k > arr[end]) {// 在[begin,indexOfMin - 1]
int begin = 0;
end = indexOfMin - 1;
ans = binarySearch(arr, begin, end, k);
} else if (k < arr[end]) {
int begin = indexOfMin;
ans = binarySearch(arr, begin, end, k);
} else {
ans = end;
}
System.out.println(ans);
}
}
private static int binarySearch(int[] arr, int begin, int end, int k) {
while (begin <= end) {
int mid = (begin + end) / 2;
if (k > arr[mid]) {
begin = mid + 1;
} else if (k < arr[mid]) {
end = mid - 1;
} else {
return mid;
}
}
return -1;
}
// 返回数组中最小数字的索引
private static int f(int[] arr) {
int begin = 0;
int end = arr.length - 1;
// 考虑数组没有旋转的特殊情况
if (arr[end] > arr[begin]) {
return begin;
}
// 当只剩两个数字进行二分查找时,右边的数字一定是最小值
while (begin + 1 < end) {
int mid = (begin + end) / 2;
if (arr[mid] < arr[end]) {// 右侧有序
end = mid;
} else {
begin = mid;
}
}
return end;
}
}