public class SequenceSearch {
public static void main(String[] args) {
int[] arr = {3, 2, 99, 0, -1, 32};
int target = -1;
int index = lookup(arr, target);
if (index == -1) {
System.out.println("没有找到目标值!");
} else {
System.out.printf("数据%d的下标为%d", target, index);
}
}
/**
* 目前找到一个目标值就进行返回,如果有多个值需要找的话,就需要将其下标存入到集合或数组中
*
* @param arr 目标数组
* @param i 要查找的目标值
* @return 如果找到就返回该目标值的下标,没找到就返回-1
*/
private static int lookup(int[] arr, int i) {
for (int j = 0; j < arr.length; j++) {
if (arr[j] == i) {
return j;
}
}
return -1;
}
}
二分查找也称折半查找,它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
一、第一步先确定数组的中间下标:mid = (left + right)/ 2,其中left为给定数组的最右边下标0,right为给定数组的长度-1;
二、将要查找的目标值targetValue和arr[mid]相比较:
如果targetValue>arr[mid],说明要查找的数在mid的右边,需要向右递归查找;
如果targetValue<arr[mid],说明要查找的数在mid的左边,需要向左递归查找;
如果targetValue==arr[mid],则说明该值已找到,返回其下标即可
三、递归结束的条件:
找到目标值就结束递归,或是递归完整个数组后,仍然没有找到targetValue,此时也需要结束递归,即当left>right时,就需要退出。
public class BinarySearch {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int index = binarySearch_1(arr, 0, arr.length - 1, 2);
System.out.println("index == " + index);
int[] arr2 = {1, 2, 3, 4, 4, 4, 5, 6, 7, 8, 9};
List<Integer> indexList = binarySearch_2(arr2, 0, arr.length - 1, 1);
System.out.println("index == " + indexList);
}
/**
* 该方法只能找到目标数组中一个符合目标值的结果
*
* @param arr 目标数组
* @param left 左索引
* @param right 右索引
* @param targetValue 目标值
* @return 找到就返回目标值下标,没有找到就返回-1
*/
private static int binarySearch_1(int[] arr, int left, int right, int targetValue) {
//当left>right时,说明递归整个数组都没有找到要找的值,同时,如果要查的值大于最大的,小于最小的,也直接返回-1
if (left > right || targetValue > arr[arr.length - 1] || targetValue < arr[0]) {
return -1;
}
int mid = (left + right) / 2;
int midValue = arr[mid];
if (targetValue > midValue) { //向右递归
return binarySearch_1(arr, mid + 1, right, targetValue);
} else if (targetValue < midValue) { //向左递归
return binarySearch_1(arr, left, mid - 1, targetValue);
} else {
return mid;
}
}
/**
* 当数组中有多个要找的相同数值时,将所有满足的数值都查找到
*
* @param arr 目标数组
* @param left 左索引
* @param right 右索引
* @param targetValue 目标值
* @return 返回装有所有满足目标值下标的list
*/
private static List<Integer> binarySearch_2(int[] arr, int left, int right, int targetValue) {
//当left>right时,说明递归整个数组都没有找到要找的值
if (left > right || targetValue > arr[arr.length - 1] || targetValue < arr[0]) {
return new ArrayList<>();
}
int mid = (left + right) / 2;
int midValue = arr[mid];
if (targetValue > midValue) { //向右递归
return binarySearch_2(arr, mid + 1, right, targetValue);
} else if (targetValue < midValue) { //向左递归
return binarySearch_2(arr, left, mid - 1, targetValue);
} else {
List<Integer> indexList = new ArrayList<>();
//向mid索引值的左边扫描,将所有满足的元素下标都加入到indexList集合中
int temp = mid - 1;
while (true) {
if (temp < 0 || arr[temp] != targetValue) {
break;//退出
}
//否则就将temp放入到indexList中
indexList.add(temp);
temp -= 1;//将temp左移
}
indexList.add(mid);
//向mid索引值的左边扫描,将所有满足的元素下标都加入到indexList集合中
temp = mid + 1;
while (true) {
if (temp > arr.length - 1 || arr[temp] != targetValue) {
break;//退出
}
//否则就将temp放入到indexList中
indexList.add(temp);
temp += 1;//将temp左移
}
return indexList;
}
}
}
插值查找,有序表的一种查找方式。插值查找是根据查找关键字与查找表中最大最小记录关键字比较后的查找方法。插值查找基于二分查找,将查找点的选择改进为自适应选择,提高查找效率。
即将二分查找中的公式改为:
mid=left+(right-left)*(targetValue-arr[left])/(arr[right]-arr[left])
public class InterpolationSearch {
public static void main(String[] args) {
int[] arr = {1, 5, 6, 8, 9, 11};
int index = interpolationSearch(arr, 0, arr.length - 1, 5);
System.out.println("index == " + index);
}
/**
* @param arr 目标数组
* @param left 左索引
* @param right 右索引
* @param targetValue 目标值
* @return 找到就返回目标值下标,没有找到就返回-1
*/
private static int interpolationSearch(int[] arr, int left, int right, int targetValue) {
//当left>right时,说明递归整个数组都没有找到要找的值,同时,如果要查的值大于最大的,小于最小的,也直接返回-1
if (left > right || targetValue > arr[arr.length - 1] || targetValue < arr[0]) {
return -1;
}
//与二分查找相比,插值查找的主要变化就在于求中间值的算法变化
int mid = left + (right - left) * (targetValue - arr[left]) / (arr[right] - arr[left]);
int midValue = arr[mid];
if (targetValue > midValue) { //向右递归
return interpolationSearch(arr, mid + 1, right, targetValue);
} else if (targetValue < midValue) { //向左递归
return interpolationSearch(arr, left, mid - 1, targetValue);
} else {
return mid;
}
}
}
斐波那契搜索就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数 F[n], 将原查找表扩展为长度为F【n】(如果要补充元素,则补充重复最后一个元素,直到满足F[n]个元素),完成后进行斐波那契分割,即F[n]个元素分割为前半部分F[n-1]个元素,后半部分F[n-2]个元素,找出要查找的元素在哪一部分并递归,直到找到。
斐波那契查找与二分查找和插值查找相似,仅仅改变了中间节点mid的位置,mid不再是中间或插值得到,而是位于黄金分割点附近,即mid=low+F(k-1)-1
(F代表斐波那契数列),入下图所示:
public class FibonacciSearch {
public static int maxSize = 20;
public static void main(String[] args) {
int[] arr = {1, 5, 6, 8, 9, 11};
System.out.println("index == " + fibonacciSearch(arr, 6));
}
/**
* 因为在斐波那契查找算法中,获取mid时需要用到:mid=low+F(k-1)-1,需要使用到斐波那契数列,因此需要先构建一个斐波那契数列
*
* @return 使用非递归的方式获取一个斐波那契数列
*/
public static int[] fib() {
int[] f = new int[maxSize];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < maxSize; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
/**
* @param arr 目标数组
* @param targetValue 目标值
* @return 返回目标值对应下标,如果没找到就返回-1
*/
private static int fibonacciSearch(int[] arr, int targetValue) {
int low = 0;
int high = arr.length - 1;
int k = 0;//表示斐波那契分割数值的下标
int mid = 0;//存放mid值
int[] f = fib();//获取斐波那契数列
//获取菲波那切分割数值的下标
while (high > f[k] - 1) {
k++;
}
//因为f[k]的值可能大于arr的长度,因此需要使用Arrays工具类,构造一个新的数组,并指向temp[],不足的部分会使用0填充
int[] temp = Arrays.copyOf(arr, f[k]);
//实际上需要使用arr数组的最后元素去填充temp数组中0的部分。eg:temp={1,2,3,0,0} =》temp={1,2,3,3,3};
for (int i = high + 1; i < temp.length; i++) {
temp[i] = arr[high];
}
//使用while来循环处理,找到目标数targetValue
while (low < high) {
mid = low + f[k - 1] - 1;
if (targetValue < temp[mid]) { //此时需要向数组的左边继续查找
high = mid - 1;
//为什么要k--:因为全部元素 = 头部元素+尾部元素;f[k]=f[k-1]+f[k-2]
k--;
} else if (targetValue > temp[mid]) { //此时需要向数组的右边继续查找
low = mid + 1;
k -= 2;
} else {
//此时已经找到该值,需要确定,返回的是哪个下标
if (mid <= high) {
return mid;
} else {
return high;
}
}
}
return -1;
}
}
因篇幅问题不能全部显示,请点此查看更多更全内容