前情提要:二分查找也叫折半查找,即在一个有序数组中通过每次将寻找的范围折半的方式来查找数据
解析即在代码里:
/**
* @author hongda
* 2020-04-09-17
* <p>
* 使用二分查找的前提是该数组是有序的
*/
public class BinarySearch {
public static void main(String[] args) {
int[] arr = {1, 8, 10, 89, 1234};
// int i = binarySearch(arr, 0, arr.length - 1, 89);
// System.out.println("index=" + i)
ArrayList<Integer> list = binarySearch2(arr, 0, arr.length - 1, 1000);
System.out.println("resIndexList" + list);
}
/**
* @param arr 数组
* @param left 左边的索引
* @param right 右边的索引
* @param findVal 要查找的值
* @return 如果找到就返回下标, 没有找到就返回-1
*/
public static int binarySearch(int[] arr, int left, int right, int findVal) {
//设立一个中间值,这个值在二分查找里就是数组的中间
int mid = (left + right) / 2;
int midVal = arr[mid];
//当left>right时,说明递归了整个数组,但是没有找到
if (left > right) {
return -1;
}
//既然中间的这个数已经是不等于midVal了,那么把它抛弃也无可厚非
//所有下面的边界分别是mid+1和mid-1
if (findVal > midVal) {
return binarySearch(arr, mid + 1, right, findVal);
} else if (findVal < midVal) {
return binarySearch(arr, left, mid - 1, findVal);
}
//当找到这个值的时候,就返回其所在的下标
else {
return mid;
}
}
/**
* 当一个有序数组中,有多个相同的数值时,如何将所有的数值都查找到
* 1.在找到mid值时,不要马上返回
* 2.向mid索引值的左边扫描,将所有满足值的下标加入到集合ArrayList中
* 3.向mid索引值的右边扫描,将所有满足值的下标加入到集合ArrayList中
*/
public static ArrayList<Integer> binarySearch2(int[] arr, int left, int right, int findVal) {
int mid = (left + right) / 2;
int midVal = arr[mid];
//当left>right时,说明递归了整个数组,但是没有找到
if (left > right) {
return new ArrayList<Integer>();
}
if (findVal > midVal) {
return binarySearch2(arr, mid + 1, right, findVal);
} else if (findVal < midVal) {
return binarySearch2(arr, left, mid - 1, findVal);
} else {
ArrayList<Integer> resIndexList = new ArrayList<>();
//向左查找
int temp = mid - 1;
while (true) {
if (temp < 0 || arr[temp] != findVal) {
break;
}
resIndexList.add(temp);
temp -= 1;
}
resIndexList.add(mid);
//向右查找
temp = mid + 1;
while (true) {
if (temp > arr.length - 1 || arr[temp] != findVal) {
break;
}
resIndexList.add(temp);
temp += 1;
}
return resIndexList;
}
}
}
插值查找:对二分查找的一种改进式算法,通过公式计算出尽可能接近的中间值,而非折半查找那样死板,从而减少递归的次数
解析附在代码中:
/**
* @param arr 数组
* @param left 左边的索引
* @param right 右边的索引
* @param findVal 要查找的值
* @return 如果找到就返回下标, 没有找到就返回-1
*/
public static int insertValueSearch(int[] arr, int left, int right, int findVal) {
//注意后面的条件必须存在,否则我们得到的mid可能越界,因为后面计算的中间值是有公式的
if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {
return -1;
}
//插值算法公式
int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
int midVal = arr[mid];
//下面的就和二分查找法思维一致
if (findVal > midVal) {
return insertValueSearch(arr, mid + 1, right, findVal);
} else if (findVal < midVal) {
return insertValueSearch(arr, left, mid - 1, findVal);
}else {
return mid;
}
}
}