一、二分查找法(无重复数)
1、算法思想
针对一个有序数据集合a(无重复数),查找元素x的下标位置。我们可以将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到了x,算法中止;如果xa[n/2],则只要在数组a的右半部搜索x。
2、时间复杂度:O(logn)
3、代码实现(Java)
(1)通过while循环实现:
public static void main(String[] args) {
int[] a = {1, 3, 5, 6, 10, 12};
System.out.println(binarySearch(a, 10));
}
public static int binarySearch(int[] a, int x) {
if (a == null || a.length == 0) {
return -1;
}
int low = 0;
int high = a.length - 1;
int mid;
while (low <= high) {
mid = low + (high - low) / 2;
if (x == a[mid]) {
return mid;
}
else if (x < a[mid]) {
high = mid - 1;
}
else {
low = mid + 1;
}
}
return -1;
}
(2)通过for循环实现
public static void main(String[] args) {
int[] a = {1, 3, 5, 6, 10, 12};
System.out.println(binarySearch(a, 10));
}
public static int binarySearch(int[] a, int x) {
if (a == null || a.length == 0) {
return -1;
}
int low = 0;
int high = a.length - 1;
int mid = low + (high - low) / 2;
for (int i = mid; low <= high; mid = low + (high - low) / 2) {
if (x == a[mid]) {
return mid;
}
else if (x < a[mid]) {
high = mid - 1;
}
else {
low = mid + 1;
}
}
return -1;
}
二、二分查找(有重复数)
如果一个n个数的有重复数的有序数据集合a,如果查找的x有连续多个,返回一个列表,保存x的起始位置;如果查找的x只有一个,则直接返回该数下标;如果没找到,则返回列表-1。
public static void main(String[] args) {
int[] a = {1, 3, 3, 3, 5, 6, 10, 12, 12};
System.out.println(binarySearch(a, 12));
}
// 有序数组,有重复数,返回查找数的起始位置
public static ArrayList<Integer> binarySearch(int[] a, int x) {
ArrayList<Integer> result = new ArrayList<>();
if (a == null || a.length == 0) {
result.add(-1);
return result;
}
int low = 0;
int high = a.length - 1;
int mid = low + (high - low) / 2;
int start, end;
boolean isExist = false; // 记录是否找到x
while(low <= high) {
mid = low + (high - low) / 2;
if (x < a[mid]) {
high = mid - 1;
}
else if (x > a[mid]) {
low = mid + 1;
}
else {
isExist = true; // 查找到至少一个x
break;
}
}
// 如果没有找到x,则直接返回-1
if (!isExist) {
result.add(-1);
return result;
}
// 如果已经找到了元素,则先从左边继续找
start = mid;
end = mid;
// 如果起始位置大于等于0且a[start] == x,则继续
while(start >= 0 && a[start] == x) {
start--;
}
start = start + 1;
// 如果终点位置小于等于数组长度且a[end] == x,则继续
while(end <= high && a[end] == x) {
end++;
}
end = end - 1;
// 如果起始位置相等,说明只找到一个数
if (start == end) {
result.add(start);
}
else {
result.add(start);
result.add(end);
}
return result;
}
三、二分查找第一个等于给定的数的下标
public static void main(String[] args) {
int[] a = {1, 3, 3, 3, 3, 6, 10, 12, 12};
System.out.println(binarySearch(a, 3));
}
public static int binarySearch(int[] a, int x) {
if (a == null || a.length == 0) {
return -1;
}
int low = 0;
int high = a.length - 1;
int mid;
while(low <= high) {
mid = low + (high - low) / 2;
if (x < a[mid]) {
high = mid - 1;
}
else if(x > a[mid]){
low = mid + 1;
}
// 否则,x == a[mid],至少找到一个值等于x
else {
// 如果中间位置等于最左边,或者a[mid - 1] != x,说明再往左没有找到等于x的值,则此时第一个等于x的值位置就是mid
if (mid == low || a[mid - 1] != x) {
return mid;
}
high = mid - 1;
}
}
return -1;
}
四、二分查找最后一个等于给定的数的下标
public static void main(String[] args) {
int[] a = {1, 3, 3, 3, 3, 6, 10, 12, 12};
System.out.println(binarySearch(a, 3));
}
public static int binarySearch(int[] a, int x) {
if (a == null || a.length == 0) {
return -1;
}
int low = 0;
int high = a.length - 1;
int mid;
while (low <= high) {
mid = low + (high - low) / 2;
if (x < a[mid]) {
high = mid - 1;
}
else if(x > a[mid]){
low = mid + 1;
}
else {
if (mid == high || a[mid + 1] != x) {
return mid;
}
low = mid + 1;
}
}
return -1;
}