目录
一、 查找算法介绍
二、二分查找
使用二分查找的前提:该数组有序
思路分析:
1.首先确定该数组的中间的下标mid= (left +right)/2
2.然后让需要查找的数findVal和ar[mid]比较
2.1 findVal>arr[mid],说明你要查找的数在mid的右边,因此需要递归的向右查找
2.2 findVal< ar[(mid],说明你要查找的数在mid的左边,因此需要递归的向左查找
2.3 findVal=ar[mid]说明找到,就返回
什么时候我们需要结束递归?
1)找到就结束递归
2)递归完整个数组,仍然没有找到findVal,也需要结束递归left> right就需要退出
/**
* 使用二分查找的前提:该数组有序
*
* @param arr 数组
* @param left 左边的索引
* @param right 右边的索引
* @param value 要查找的值
* @return 如果找到返回下标,没有找到返回-1
*/
public static int binarySearch(int[] arr, int left, int right, int value) {
//当left>right时,说明递归完整个数组,但是没有找到
if (left > right) return -1;
//中间下标
int mid = (left + right) >> 1;
//中间值
int midValue = arr[mid];
//如果
if (value > midValue) {//向右递归
return binarySearch(arr, mid + 1, right, value);
} else if (value < midValue) {//向左递归
return binarySearch(arr, left, mid - 1, value);
} else {//相等情况
return mid;
}
}
上述代码只是找到一个就直接返回了该值所在的索引,只找到了一个findValue所在的位置,下面对程序进行优化,当找到一个相等位置时,不立刻返回mid,从mid位置向左和向右查找,查找到仍然相等的值,将所有的索引加入到ArrayList进行返回。
/**
* 改进后的能够返回所有相同的值的索引
* <p>
* 1.在找到mid索引值,不要马上返回
* 2.向mid索引值的左边扫描,将所有满足这个数的元素的下标,加入到集合ArrayLis中
* 3.向mid索引值的右边扫描,将所有满足这个数的元素的下标,加入到集合ArrayLis中
*
* @param arr
* @param left
* @param right
* @param value
* @return
*/
public static ArrayList<Integer> binarySearch2(int[] arr, int left, int right, int value) {
//当left>right时,说明递归完整个数组,但是没有找到
if (left > right) return new ArrayList<Integer>();
//中间下标
int mid = (left + right) >> 1;
//中间值
int midValue = arr[mid];
//如果
if (value > midValue) {//向右递归
return binarySearch2(arr, mid + 1, right, value);
} else if (value < midValue) {//向左递归
return binarySearch2(arr, left, mid - 1, value);
} else {//相等情况
// 1.在找到mid索引值,不要马上返回
// 2.向mid索引值的左边扫描,将所有满足这个数的元素的下标,加入到集合ArrayLis中
ArrayList<Integer> indexList = new ArrayList<>();
//向左扫描
int temp = mid - 1;//向左
while (true) {
if (temp < 0 || arr[temp] != value) break;//如果左边没有和它相同的了直接退出循环
//到这里说明此事arr[temp] 是等于value的,添加进去
indexList.add(temp--);
}
indexList.add(mid);
// 3.向mid索引值的右边扫描,将所有满足这个数的元素的下标,加入到集合ArrayLis中
temp = mid + 1;
while (true) {
if (temp > arr.length - 1 || arr[temp] != value) break;//如果右边没有和它相同的了直接退出循环
//到这里说明此事arr[temp] 是等于value的,添加进去
indexList.add(temp++);
}
return indexList;
}
}
三、插值查找
/**
* 插值查找算法
*/
public static int insertValueSearch(int[] arr, int left, int right, int findVal) {
//如果findVal非常大或者非常小,明显不在里面直接返回-1
//如果不加findVal < arr[0] || findVal > arr[arr.length - 1],极有可能出现数值越界,因为得到mid的过程有findVal的参与。
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 (midVal > findVal) {
return insertValueSearch(arr, left, mid - 1, findVal);
} else if (midVal < findVal) {
return insertValueSearch(arr, mid + 1, right, findVal);
} else {
return mid;
}
}
四、斐波那契查找
说实话本人没搞明白,直接上代码!!!
//因为后面mid=low+F(k-1)+1,需要使用到斐波那契数列,因此需要先获取到一个斐波那契数列
public static int[] fib() {
int[] f = new int[20];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < 20; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
//斐波那契查找算法
/**
* @param arr
* @param key 需要查找的数
* @return 返回对应下标
*/
//非递归方式
public static int fibonacciSearch(int[] arr, int key) {
int low = 0;
int high = arr.length - 1;
int k = 0;//表示斐波那契分割数值的那个下标,就是mid=low+F(k-1)+1里面的k
int mid = 0;//存放mid值
int f[] = fib();//获取到斐波那契数列
//获取到斐波那契分割数值的那个下标
while (high > f[k] - 1) {
k++;
}
//当high<=f[k]-1时,就拿到了k值
//因为f[k]值可能大于a的长度,因此我们需要使用Arrays类,构造一个新的数组,并指向arr[]
//不足的部分,会使用0填充
//arr={1,7,10,66,1000}
int[] temp = Arrays.copyOf(arr, f[k]);
//temp={1,7,10,66,1000,0,0,0,0}不是想要的
//实际上需要使用arr[arr.length-1]填充temp,不能用0填充
for (int i = high + 1; i < temp.length; i++) {
temp[i] = arr[high];
}
//temp={1,7,10,66,1000,1000,1000,1000,1000}
//使用while来循环处理,找到我们的数key
while(low <= high){//只要这个条件满足,就可以找
mid = low + f[k - 1] -1;
if (key < temp[mid]){//我们应该继续向数组的前面查找
high = mid - 1;
k--;
}else if (key > temp[mid]){
low = mid + 1;
k-=2;
}else {
if (mid <= high){
return mid;
}else {
return high;
}
}
}
return -1;
}