二分查找
二分查找是一种极致快速的查找方式,时间复杂度仅为:O(log(n))
二分查找对数据有很高的要求,包含以下三点:
- 必须为数组结构
二分查找其实也可以依赖链表结构,不过如果使用链表接口、那么时间复杂度就会退化到O(n),把整个数据遍历一遍进行查找的效率是一样的、甚至更低,链表不适用于二分查找。 - 数据必须有序
二分查找依赖于数据的有序、因为二分法中的数据对比、取决的数据的顺序。如果数据无序,那么需要先排序 - 数据量太大或太小都不适合二分查找
如果数据量特别小、个位数或者就几十个数据,那么可以直接遍历数据进行查找;
如果数据量特别大,几百兆或者几个G,由于二分查找依赖于数据,需要连续空间,那么系统可能提供不了连续的几个G的空间
基本的二分查找算法实现
public class Bsearch {
@Test
public void testSearch() {
// 二分查找,必须有序
int[] data = new int[]{19, 55, 67, 23, 27, 8, 11, 33, 45, 98};
// 1、快排
quickSort(data, 0, data.length - 1);
System.out.println(Arrays.toString(data));
// 2、二分查找
int value = 23;
System.out.println(value + " : 在数组data中的index为 : " + bSearch(data, 0, data.length - 1, value));
}
/**
* 二分查找
*/
private int bSearch(int[] data, int low, int high, int value) {
if (low > high) {
return -1;
}
int mid = low + ((high - low) >> 1);
if (value == data[mid]) {
return mid;
} else if (value > data[mid]) {
return bSearch(data, mid + 1, high, value);
} else {
return bSearch(data, low, mid - 1, value);
}
}
/**
* 快排逻辑
*/
private void quickSort(int[] data, int p, int r) {
if (p >= r) {
return;
}
int q = partition(data, p, r);
quickSort(data, p, q - 1);
quickSort(data, q + 1, r);
}
private int partition(int[] data, int p, int r) {
int i = p;
int povit = data[r];
for (int j = p; j < r; j++) {
if (data[j] < povit) {
if (i != j) {
int tmp = data[i];
data[i] = data[j];
data[j] = tmp;
}
i++;
}
}
int tmp = data[r];
data[r] = data[i];
data[i] = tmp;
return i;
}
}
使用二分查找计算正整数的平方根
public class Sqrt {
@Test
public void test() {
int data = 8;
System.out.println(mySqrt(data));
}
private int mySqrt(int x) {
if (x < 2) {
return x;
} else {
return sqrt(x, 0, x);
}
}
private int sqrt(int data, int low, int high) {
if(low > high){
return high;
}
int mid = low + ((high - low) >> 1);
if (mid > data / mid) {
return sqrt(data, low, mid - 1);
} else if (mid < data / mid) {
return sqrt(data, mid + 1, high);
} else {
return mid;
}
}
}
二分查找的变体
查找第一个指定元素的位置
如果数组中存在多个重复的指定元素、请返回第一个指定元素的index
private int bSearch(int[] data, int low, int high, int value) {
if (low > high) {
return -1;
}
int mid = low + ((high - low) >> 1);
if (value == data[mid]) {
if (data[mid - 1] != value) {
return mid;
} else {
return bSearch(data, low, mid - 1, value);
}
} else if (value > data[mid]) {
return bSearch(data, mid + 1, high, value);
} else {
return bSearch(data, low, mid - 1, value);
}
}
查找最后一个指定元素的位置
private int bSearch(int[] data, int low, int high, int value) {
if (low > high) {
return -1;
}
int mid = low + ((high - low) >> 1);
if (value == data[mid]) {
if (data[mid + 1] != value) {
return mid;
} else {
return bSearch(data, mid + 1, high, value);
}
} else if (value > data[mid]) {
return bSearch(data, mid + 1, high, value);
} else {
return bSearch(data, low, mid - 1, value);
}
}
查找第一个大于等于给定值的元素位置
public class FirstLarge {
@Test
public void test() {
int[] data = new int[]{2, 5, 8, 11, 16};
int value = 11;
System.out.println(firstLarge(data, value, 0, data.length - 1));
}
private int firstLarge(int[] data, int value, int low, int high) {
if (low > high) {
return low;
}
int mid = low + ((high - low) >> 1);
if (value > data[mid]) {
return firstLarge(data, value, mid + 1, high);
} else if (value < data[mid]) {
return firstLarge(data, value, low, mid - 1);
} else {
return mid;
}
}
}
查找最后一个小于等于给定值的元素位置
public class LastSmall {
@Test
public void test() {
int[] data = new int[]{2, 6, 9, 11, 16};
int value = 10;
System.out.println(lastSmall(data, value, 0, data.length - 1));
}
private int lastSmall(int[] data, int value, int low, int high) {
if (low > high) {
return high;
}
int mid = low + ((high - low) >> 1);
if (value > data[mid]) {
return lastSmall(data, value, mid + 1, high);
} else if (value < data[mid]) {
return lastSmall(data, value, low, mid - 1);
} else {
return mid;
}
}
}