查找算法介绍
在java中常用的查找算法一共有四种
1、顺序(线性)查找
2、二分查找(折半查找)
3、插值查找
4、斐波那契查找
一、顺序查找
基本原理:对于任意一个序列以及一个给定的元素,将给定元素与序列中元素依次比较,直到找出与给定关键字相同的元素,或者将序列中的元素与其都比较完为止。
代码如下:
public class queSearch {
public static void main(String[] args) {
int[] arr = {5,11,3,64,95,11,2,35,4};
List list = search(arr,11);
if (list.size() == 0){
System.out.println("没有找到您要搜索的数据索引位置");
}else{
System.out.println("您要搜索数据的位置为:" + list.toString());
}
}
public static List search(int[] arr,int value){
List list = new ArrayList();
for (int i=0;i<arr.length;i++){
if (value == arr[i]){
list.add(i);
}
}
return list;
}
}
结果如下:
二、二分查找
思路分析:
所需要查找的序列必须是有序的
1、首先确定该数组的中间的下标,mid = (left + right) / 2;
2、然后让需要查找的数findVal与arr[mid]比较
2.1如果findVal < arr[mid],说明需要查找的数在mid左边,因此需要继续递归向左查找
2.2如果findVal > arr[mid],说明需要查找的数在mid右边,因此需要继续递归向右查找
2.3如果findVal = arr[mid],说明需要查找的数位置就是mid,直接返回结果
结束递归的条件:
1.找到就结束递归
2.递归完整个数组,仍然没有找到,就需要结束,此时left>right递归结束
代码如下:
public class BinarySearch {
public static void main(String[] args) {
int[] arr = {153,654,987,1000,1000,1023,1254,2148};
List<Integer> list = search(arr,0,arr.length-1,1000);
System.out.println(list);
}
public static List<Integer> search(int[] arr,int left,int right,int findVal){
if (left > right){
return new ArrayList();
}
int mid = (left + right) / 2;
int midVal = arr[mid];
List<Integer> indexVal = new ArrayList<>();
if (findVal < midVal){
return search(arr,left,mid - 1,findVal);
}else if (findVal > midVal){
return search(arr,mid + 1,right,findVal);
}else{
int index = mid;
while (true){
if ((index-1)<0 || arr[index-1] != findVal){
break;
}
index = index - 1;
indexVal.add(index);
}
indexVal.add(mid);
index = mid;
while (true){
if ((index+1)>(arr.length-1) || arr[index+1] != findVal){
break;
}
index = index + 1;
indexVal.add(index);
}
}
return indexVal;
}
}
结果如下:
三、插值查找
插值查找原理介绍:
(1)插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid处开始查找。
(2)将折半查找中的求mid索引的公式,left表示左边索引,right表示右边索引,key就是要寻找的数据值,则mid取值如下:mid = (left + right)/2 = left + 1/2(right - left)
将上述折半查找的公式改写为:mid = left + {(key - arr[left])/(arr[right]-arr[left])}*(right - left)
代码如下:
public class InsertSearch {
public static void main(String[] args) {
int[] arr = new int[100];
for (int i=0;i<100;i++){
arr[i] = i;
}
int index = insertSearch(arr,0,arr.length -1,90);
System.out.println(index);
}
public static int insertSearch(int[] arr,int left,int right,int key){
System.out.println("进行插值查找");
if (left > right || key < arr[0] || key > arr[arr.length -1]){
return -1;
}
int mid = left + (right - left) * (key - arr[left])/(arr[right] - arr[left]);
int midVal = arr[mid];
if (key > midVal){
return insertSearch(arr,mid + 1,right,key);
}else if(key < midVal){
return insertSearch(arr,0,mid -1,key);
}else {
return mid;
}
}
}
结果如下:
可以看出如果有序序列的波动不大,分布比较均匀的话使用插值查找的速度十分快。
四、斐波那契查找(黄金分割法)
基本介绍:
(1)黄金分割点是指把一条线段分割为两部分,使其中一部分与全长之比等于另一部分与这部分之比。取其前三位数字的近似值是0.618.由于按此比例设计的造型十分美丽,因此称为黄金分割,也称为中外比。这是一个神奇的数字,会带来意想不到的效果。
(2)斐波那契数列{1, 1,2, 3,5, 8,13, 21, 34, 55}发现斐波那契数列的两个相邻数的比例无限接近黄金分割值0.618
基本原理介绍:
斐波那契查找的原理与前两种相似,仅仅改变中间节点(mid)的位置,mid不再是中间或者插值得到,而是位于黄金分割点的附近,即mid = left + F(k-1)-1,如下图所示:
对F(k-1)-1的理解:
(1)由斐波那契数列F[k] = F[k-1] + F[k-2]的性质,可以得到F[k]-1 = F[k-1]-1 + F[k-2]-1 +1,该式说明,只要序列的长度为F[k]-1,则可以将该表分成长度为F[k-1]-1和F[k-2]-1的两段,从而中间值
mid = left + F(k-1)-1
(2)类似的,每一子段也可以用相同的方式分割
(3)但顺序表长度n不一定刚好等于F[k]-1,所以需要将原来的顺序表长度n增加至F[k]-1。这里的k值只要能使F[k]-1恰好大于或等于n即可。
代码如下:
public class FibonacciSearch {
public static int maxSize = 20;
public static void main(String[] args) {
int[] arr = {1,8,13,45,69,78,123};
int index = fibSearch(arr,69);
System.out.println("index:" + index);
}
public static int[] fib(){
int[] f = new int[maxSize];
f[0] = 1;
f[1] = 1;
for (int i=2;i<f.length;i++){
f[i] = f[i-1] + f[i-2];
}
return f;
}
public static int fibSearch(int[] arr,int key){
int left = 0;
int right = arr.length - 1;
int[] f = fib();
int k = 0;
int mid = 0;
while (right > f[k] - 1){
k++;
}
int[] temp = Arrays.copyOf(arr,f[k]);
for(int i=right+1;i<f[k];i++){
temp[i] = arr[right];
}
while (left <= right){
mid = left + f[k-1]-1;
if (temp[mid] > key){
right = mid -1;
k--;
}else if(temp[mid] < key){
left = mid + 1;
k -=2;
}else{
if (mid <= right){
return mid;
}else{
return right;
}
}
}
return -1;
}
}
结果如下: