查找算法及应用
常用查找算法包括顺序/线性查找、二分/折半查找、插值查找、斐波那契查找。
-
线性查找
逐一比对,发现有相同值返回即可。
-
二分查找
前提是数组有序。如果数据是连续的情况下可以使用插值查找。
-
插值查找
插值查找算法类似二分查找,不同的是插值查找每次从自适应中间位置处开始查找。数据比较连续情况下实用。将折半查找中的求mid索引的公式改进,low表示左边索引left,high表示右边索引right,key表示查找的值。
m i d = l o w + h i g h 2 = l o w + 1 2 ( l o w + h i g h ) mid = \frac{low+high}{2} = low + \frac{1}{2}(low + high) mid=2low+high=low+21(low+high)m i d = l o w + k e y − a [ l o w ] a [ h i g h ] − a [ l o w ] ( h i g h − l o w ) mid = low + \frac{key - a[low]}{a[high] - a[low]}(high - low) mid=low+a[high]−a[low]key−a[low](high−low)
-
斐波那契查找
黄金分割点是指把一条线段分成两段,使其中一部分与全长之比等于另一部分与这一部分之比,取其前三位数字的近似值是0.618。斐波那契数列的两个相邻数的比例,无线接近黄金分割值0.618。斐波那契查找原理是改变中间节点mid的位置,mid=low+F(k-1)-1,其中F代表斐波那契数列。
/*** 抽象父类*/
public abstract class SearchParent {protected static int[] arr = {1, 8, 10, 89, 1000, 1000, 1000, 1234};//在数组中查找指定值的位置abstract int search(int[] arr, int val);
}
/*** 线性查找*/
public class SimpleSearch extends SearchParent {public static void main(String[] args) {System.out.println("89的下标是:" + new SimpleSearch().search(arr, 89));}@Overrideint search(int[] arr, int val) {for (int i=0;i<arr.length;i++){if (val == arr[i]) {return i;}} return -1;}
}
import java.util.ArrayList;
import java.util.List;/*** 折半查找*/
public class BinarySearch extends SearchParent {public static void main(String[] args) {integerOverflow();System.out.println("The index of 1000 is " + new BinarySearch().search(arr, 1000));}@Overrideint search(int[] arr, int val) {int left = 0;int right = arr.length - 1;return binarySearch(arr, left, right, val);}//只能查找到第一个匹配的值private static int binarySearch(int[] arr, int left, int right, int val){int mid = (left + right)/2;int tmp = arr[mid];if(left > right){return -1;}if(val > tmp){return binarySearch(arr, mid+1, right, val);}else if(val < tmp){return binarySearch(arr, left, mid-1, val);}else{return mid;}}//折半查找非递归方式实现private static int binarySearch(int[] arr, int val){int left = 0;int right = arr.length - 1;while(left <= right){int mid = (left + right)/2;if(arr[mid] > val){right = mid - 1;}else if(arr[mid] < val){left = mid + 1;}else{return mid;}}return -1;}//查找所有符和的值private static List<Integer> binary2Search(int[] arr, int left, int right, int val){int mid = (left + right)/2;int tmp = arr[mid];if(left > right){return null;}if(val > tmp){return binary2Search(arr, mid + 1, right, val);}else if(val < tmp){return binary2Search(arr, left, mid-1, val);}else{List<Integer> result = new ArrayList<>(arr.length);int temp = mid-1;while(temp >= 0 && arr[temp] == val) {result.add(temp);temp--;}result.add(mid);temp = mid + 1;while(temp <= arr.length-1 && arr[temp] == val){result.add(temp);temp++;}return result;}}//获取中间索引可能会出现整数溢出private static void integerOverflow(){int l = 0;int r = Integer.MAX_VALUE - 1;int m = (l+r)/2;System.out.println(m);l = m + 1;//m = (l+r)/2; 会导致整数溢出//m = l + (r-l)/2; 除法效率较差m = (l+r)>>>1;System.out.println(m);}
}
/*** 插值查找*/
public class InsertValSearch extends SearchParent {public static void main(String[] args) {System.out.println("1000的下标是:" + new InsertValSearch().search(arr, 1000));}@Overrideint search(int[] arr, int val) {int left = 0;int right = arr.length - 1;return insertSearchValue(arr, left, right, val);}private int insertSearchValue(int[] arr, int left, int right, int val) {if(left > right || val < arr[0] || val > arr[arr.length-1]){return -1;}int mid = left + (right-left)*(val-arr[left])/(arr[right]-arr[left]);int temp = arr[mid];if(val > temp){return insertSearchValue(arr, mid+1, right, val);}else if(val < temp){return insertSearchValue(arr, left, mid+1, val);}else{return mid;}}
}
import java.util.Arrays;/*** 斐波那契查找*/
public class FibonacciSearch extends SearchParent {private static final int MAXSIZE = 20;public static void main(String[] args) {System.out.println(new FibonacciSearch().search(arr, 1000));}//生成斐波那契数组private int[] fib(){int[] f = new int[MAXSIZE];f[0] = 1;f[1] = 1;for(int i=2;i<MAXSIZE;i++){f[i] = f[i-2] + f[i-2];}return f;}@Overrideint search(int[] arr, int val) {int low = 0;int high = arr.length - 1;int k = 0; //表示斐波那契分割值的下标int mid = 0;int[] f = fib(); //获取斐波那契分割数值的下标//找到数组长度对应的斐波那契数列中对应的元素F(n)的值while(high > f[k] - 1){k++;}int[] temp = Arrays.copyOf(arr, f[k]); //F(k)的值可能大于arr的长度,不足部分使用0填充//使用数组的最后一个数填充for(int i=high+1;i<temp.length;i++){temp[i] = arr[high];}while(low <= high){mid = low + f[k-1] - 1;if(val < temp[mid]){//全部元素=前面元素+后面元素,f[k]=f[k-1]+f[k-2]//因为前面有f[k-1]个元素,所以可以继续拆分f[k-1]=f[k-2]+f[k-3]high = mid - 1;k-=1;}else if(val > temp[mid]){low = mid + 1;//因为后面有f[k-2]个元素,所以可以继续拆分k-=2;}else{if(mid <= high){ //如果是原查找表中的元素return mid;}else{return high; //如果是填充值}}}return -1;}
}