目录
查找算法
介绍
在Java中,我们常用的查找有四种:
1)顺序(线性)查找
2)二分查找/折半查找
3)插值查找
4)斐波那契查找
顺序(线性)查找
代码实现
package search;
public class SeqSearch {
public static void main(String[] args) {
int arr[]={1,9,11,-1,34,89};//没有顺序的数组
int index=seqSearch(arr, 11);
if (index==-1) {
System.out.println("没有找到");
}else {
System.out.println("找到,下标为="+index);
}
}
/*这里我们实现的线性查找是找到一个满足条件的值,就返回*/
public static int seqSearch(int[] arr,int value) {
//线性查找是逐一比对,发现有相同值,就返回下标
for (int i = 0; i < arr.length; i++) {
if (arr[i]==value) {
return i;
}
}
return -1;
}
}
二分查找算法
案例
请对一个有序数组进行二分查找{1,8,10,89,1000,1234},输入一个数看看该数组是否存在此数,并且求出下标,如果没有就提示"没有这个数"。
思路
1.首先确定该数组的中间的下标
mid=(left+right)/2
2.然后让需要查找的数findVal和arr[mid]比较
2.1findVal>arr[mid],说明你要查找的数在mid的右边,因此需要递归的向右查找
2.2findVal<arr[mid],说明你要查找的数在mid的左边,因此需要递归的向左查找
2.3findVal==arr[mid],说明找到,就返回
//什么时候我们需要结束递归
1)找到就结束递归
2)递归完整个数组,仍然没有找到findVal,也需要结束递归当left>right就需要退出
代码实现
package search;
//注意:使用二分查找的前提是该数组是有序的
public class BinarySearch {
public static void main(String[] args) {
int arr[]= {1,8,10,89,1000,1234};
int resIndex=binarySearch(arr,0,arr.length-1, 1234);
System.out.println("resIndex="+resIndex);
}
//二分查找算法
/* arr 数组
* left 左边的索引
* right 右边的索引
* findVal 要查找的值
* 如果找到就返回下标,如果没有找到,就返回-1
* */
public static int binarySearch(int[] arr,int left,int right,int findVal) {
//当left>right时,说明递归整个数组,但是没有找到
if (left>right) {
return -1;
}
int mid=(left+right)/2;
int midVal=arr[mid];
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,8,10,89,1000,1000,1234}当一个有序数组中,
有多个相同的数值时,如何将所有的数值都查找到,比如这里的1000
代码实现
package search;
import java.awt.image.RescaleOp;
import java.util.ArrayList;
import java.util.List;
//注意:使用二分查找的前提是该数组是有序的
public class BinarySearch {
public static void main(String[] args) {
int arr[]= {1,8,10,89,1000,1000,1234};
// int resIndex=binarySearch(arr,0,arr.length-1, 1234);
// System.out.println("resIndex="+resIndex);
List<Integer> resIndexList=binarySearch2(arr, 0,arr.length-1,1000);
System.out.println("resIndexList="+resIndexList);
}
//二分查找算法
/* arr 数组
* left 左边的索引
* right 右边的索引
* findVal 要查找的值
* 如果找到就返回下标,如果没有找到,就返回-1
* */
public static int binarySearch(int[] arr,int left,int right,int findVal) {
//当left>right时,说明递归整个数组,但是没有找到
if (left>right) {
return -1;
}
int mid=(left+right)/2;
int midVal=arr[mid];
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,8,10,89,1000,1000,1234}当一个有序数组中,
* 有多个相同的数值时,如何将所有的数值都查找到,比如这里的1000
*
*思路分析:
*1.在找到mid值,不要马上返回
*2.向mid索引值的左边扫描,将所有满足1000的元素的下标,加入到集合ArrayList
*3.向mid索引值的右边扫描,将所有满足1000的元素的下标,加入到集合ArrayList
*4.将Arraylist返回
*/
public static List binarySearch2(int[] arr,int left,int right,int findVal) {
//当left>right时,说明递归整个数组,但是没有找到
if (left>right) {
return new ArrayList<Integer>();
}
int mid=(left+right)/2;
int midVal=arr[mid];
if (findVal>midVal) {//向右递归
return binarySearch2(arr, mid+1, right, findVal);
}else if (findVal<midVal) {//向左递归
return binarySearch2(arr, left, mid-1, findVal);
}else {
// 思路分析:
// *1.在找到mid值,不要马上返回
// *2.向mid索引值的左边扫描,将所有满足1000的元素的下标,加入到集合ArrayList
// *3.向mid索引值的右边扫描,将所有满足1000的元素的下标,加入到集合ArrayList
// *4.将Arraylist返回
List<Integer> resIndexList=new ArrayList<Integer>();
//向mid索引值的左边扫描,将所有满足1000的元素的下标,加入到集合ArrayList
int temp=mid-1;
while (true) {
if (temp<0||arr[temp]!=findVal) {//退出
break;
}
//否则,就temp放入到resIndexlist
resIndexList.add(temp);
temp-=1;//temp左移
}
resIndexList.add(mid);
//向mid索引值的右边扫描,将所有满足1000的元素的下标,加入到集合ArrayList
temp=mid-1;
while (true) {
if (temp>arr.length-1||arr[temp]!=findVal) {//退出
break;
}
//否则,就temp放入到resIndexlist
resIndexList.add(temp);
temp+=1;//temp右移
}
return resIndexList;
}
}
}
运行效果
插值算法工作原理
1)插值查找算法类似于二分查找,不同的是插值查找每次自适应mid处开始查找
2)将折半查找中的求mid索引的公式,low表示左边索引left,high表示右边索引right.
key就是前面将的findVal
3)int midIndex=low+(high-low)*(key-arr[low])/arr[high]-arr[low]);//插值索引
对应前面的代码公式:
int mid=left+(right-left)*(findVal-arr[left])/(arr[right]-arr[left]
4)举例说明插值算法1-100的数组
举例说明
数组arr={1,2,3,...,100}
假如我们需要查找的值1
使用二分查找的话,我们需要多次递归,才能找到1
使用插值查找算法
int mid=left+(right-left)*(findVal-arr[left])/(arr[right]-arr[left]
int mid=0+(99-0)*(1-1)/(100-1)=0+99*0/99=0
比如我们查找的值100
int mid=0+(99-0)*(100-1)/(100-1)=0+99*99/99=0+99=99
代码实现
package search;
import java.util.Arrays;
public class InsertValueSearch {
public static void main(String[] args) {
int [] arr=new int[100];
for (int i = 1; i <= 100; i++) {
arr[i]=i+1;
}
int index=insertValueSearch(arr, 0, arr.length-1,78);
System.out.println("index="+index);
//System.out.println(Arrays.toString(arr));
}
//编写插值查找算法
/*
* arr 数组
* left 左边索引
* right 右边索引
* findVal 查找值
* 如果找到,就返回对应的下标,如果没有找到,返回-1
*/
public static int insertValueSearch(int [] arr,int left,int right,int findVal) {
System.out.println("二分查找被调用~~~");
//注意:findVal<arr[0]和findVal>arr.length-1必须需要
//否则我们得到的mid可能越界
if (left>right||findVal<arr[0]||findVal>arr.length-1) {
return -1;
}
//求出mid
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;
}
}
}
注意事项
1)对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找,速度较快。
2)关键字分布不均匀的情况下,该方法不一定比折半查找要好。