1、线性查找
线性查找是一种非常简单的查找方式。查找思路是:从数组的一个元素出发,一个个地和要查找的值进行比较,如果发现有相同的元素就返回该元素的下标。反之返回-1(未找到)
public class Demo1 {public static void main(String[] args) {int[] arr = {1, 11, -1, 0, 55};int result = seqSearch(arr, 1);if(result == -1) {System.out.println("数组中没有该元素");}else {System.out.println("该元素在数组的下标是:" + result);}}/*** 线性查找* @param arr 查找的数组* @param num 待查找的数字* @return 数字的索引*/public static int seqSearch(int[] arr, int num) {for(int i = 0; i<arr.length; i++) {if(arr[i] == num) {return i;}}return -1;}}
2、二分查找
进行二分查找的数组必须为有序数组
- 设置一个指向中间元素下标的变量mid,mid=(left + right)/2
- 让要查找的元素和数组mid下标的元素进行比较
- 如果查找的元素大于arr[mid],则left变为mid后面一个元素的下标
- 如果查找的元素小于arr[mid],则right变为mid前一个元素的下标
- 如果查找的元素等于arr[mid],则mid就是要查找元素所在的位置
- 当left > rigth时,说明元素不在该数组中


//注意:使用二分查找的前提是 该数组是有序的.public class BinarySearch {public static void main(String[] args) {int arr[] = { 1, 8, 10, 89,1000,1000, 1234 };int result = binarySearch(arr,0, arr.length - 1,10000);if(result == -1){System.out.println("数组中没有该元素");}else {System.out.println("该元素在数组的下标是:" + result);}}/*** 二分查找算法* @param arr 数组* @param left 左边的索引* @param right 右边的索引* @param findVal 要查找的值* @return 如果找到就返回下标,如果没有找到,就返回 -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;}}}
但是有可能要查找的元素有多个。这时就需要在找到一个元素后,不要立即返回,而是扫描其左边和右边的元素,将所有相同元素的下标保存到一个数组中,然后一起返回
//注意:使用二分查找的前提是 该数组是有序的.public class BinarySearch {public static void main(String[] args) {int arr[] = { 1, 8,10, 89,1000,1000, 1234 };List<Integer> reslist = binarySearch2(arr,0, arr.length - 1,1000);if (reslist.size() == 0){System.out.println("数组中没有该元素");}else {System.out.println("该元素在数组的下标是:" + reslist);}}//完成一个课后思考题:/** 课后思考题: {1,8, 10, 89, 1000, 1000,1234} 当一个有序数组中,* 有多个相同的数值时,如何将所有的数值都查找到,比如这里的 1000** 思路分析* 1. 在找到mid 索引值,不要马上返回* 2. 向mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList* 3. 向mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList* 4. 将Arraylist返回*/public static List<Integer> 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 {List<Integer> resultIndexList = new ArrayList<>();//向mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayListint temp = mid -1;while (true){if (temp < 0 || arr[temp] != findVal) {break;}//否则,就temp 放入到 resIndexlistresultIndexList.add(temp);temp--;}resultIndexList.add(mid);//向mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayListtemp = mid + 1;while (true){if (temp > arr.length - 1 || arr[temp] != findVal) {break;}resultIndexList.add(temp);temp++;}return resultIndexList;}}}
3、插值查找
在二分查找中,如果我们 要找的元素位于数组的最前端或者最后段,这时的查找效率是很低的。所以在二分查找至上,引入了插值查找,也是一种基于有序数组的查找方式
插值查找与二分查找的区别是:插值查找使用了一种自适应算法,用这种算法来计算mid。
mid的值在两种查找算法中的求法:
- 二分查找:mid = (left + right)/2
- 插值查找:mid = left + (right - left) * (num - arr[left]) / (arr[right] - arr[left])
- 其中num为要查找的那个值

public class InsertValueSearch {public static void main(String[] args) {int[] arr = new int[100];for (int i = 0; i < 100; i++) {arr[i] = i;}//int arr[] = { 1, 8,10, 89,1000,1000, 1234 };List<Integer> resListBinary = binarySearch2(arr,0, arr.length - 1,66 );List<Integer> reslistInsert = insertValueSearch(arr,0, arr.length - 1,66);if (resListBinary.size() == 0){System.out.println("数组中没有该元素");}else {System.out.println("二分查找:该元素在数组的下标是:" + resListBinary);}System.out.println("*************************");if (reslistInsert.size() == 0){System.out.println("数组中没有该元素");}else {System.out.println("插值查找:该元素在数组的下标是:" + reslistInsert);}}//编写插值查找算法//说明:插值查找算法,也要求数组是有序的/***插值查找* @param arr 数组* @param left 左边索引* @param right 右边索引* @param findVal 查找值* @return 如果找到,就返回对应的下标,如果没有找到,返回-1*/public static List<Integer> insertValueSearch(int[] arr,int left,int right,int findVal){System.out.println("插值查找次数~~");//注意:findVal < arr[0] 和 findVal > arr[arr.length - 1] 必须需要//否则我们得到的 mid 可能越界if(left > right || findVal < arr[0] || findVal > arr[arr.length - 1]){return new ArrayList<Integer>();}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 {List<Integer> resultIndexList = new ArrayList<>();//向mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayListint temp = mid -1;while (true){if (temp < 0 || arr[temp] != findVal) {break;}//否则,就temp 放入到 resIndexlistresultIndexList.add(temp);temp--;}resultIndexList.add(mid);//向mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayListtemp = mid + 1;while (true){if (temp > arr.length - 1 || arr[temp] != findVal) {break;}resultIndexList.add(temp);temp++;}return resultIndexList;}}//二分查找public static List<Integer> binarySearch2(int[] arr, int left, int right, int findVal){System.out.println("二分查找被调用~");// 当 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 {List<Integer> resultIndexList = new ArrayList<>();//向mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayListint temp = mid -1;while (true){if (temp < 0 || arr[temp] != findVal) {break;}//否则,就temp 放入到 resIndexlistresultIndexList.add(temp);temp--;}resultIndexList.add(mid);//向mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayListtemp = mid + 1;while (true){if (temp > arr.length - 1 || arr[temp] != findVal) {break;}resultIndexList.add(temp);temp++;}return resultIndexList;}}}
4、斐波那契(黄金分割)查找

代码:
package com.luyi.DataStructures.recursion;import java.lang.reflect.Array;import java.util.Arrays;/*** 斐波那契查找*/public class FibonacciSearch {public static int maxSize = 20;public static void main(String[] args) {int[] arr = {1,8,10,89,1000,1234};System.out.println(fibSearch(arr, 1234));// 5}// 因为后面我们mid = low+F(k - 1)-1. 需要使用斐波那契数列, 因此我们需要先获取到斐波那契数列// 用非递归的方法得到一个大小为20的斐波那契数列public static int[] fbn(){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 a* @param key 需要查找的值* @return 返回对应的下标 没有则返回-1*/public static int fibSearch(int[] a, int key){int low = 0;int high = a.length -1;int k = 0; // 表示斐波那契分割数值的下标 mid = low+F(k - 1)-1int mid = 0;int[] f = fbn(); // 获取斐波那契数列// 获取斐波那契分割数值的下标while (high > f[k]-1){k++;}// 因为 f[k] 可能大于数组a 的长度 需要一个Array类 构造一个新的数组 并指向temp[]int[] temp = Arrays.copyOf(a, f[k]); // 多出来的部分用0补起来// 实际上需要使用a最后的数填充temp// 举例 :// temp:{1,8,10,89,1000,1234,0,0} =>temp:{1,8,10,89,1000,1234,1234,1234}for (int i = high + 1;i < temp.length; i++){temp[i] = a[high];}// 使用while循环查找我们的keywhile (low <= high) {mid = low + f[k-1]-1;if (key < temp[mid]){//说明应该向数组的前一部分查找(左边)high = mid -1;// 为什么是k--// 1 全部元素 = 前面的元素 + 后边的元素// 2 f[k] = f[k-1] + f[k-2];// 因为前面有f[k-1]个元素,所以我们继续拆分 f[k-1] = f[k-2] + f[k-3];// 即在f[k-1] 的前面继续查找 k--// 即下次在循环的时候 mid = f[k-1-1] -1k--;}else if ( key > temp[mid]){ // 我们继续向数组的右边查找low = mid + 1;// 为什么是k-2// 1 全部元素 = 前面的元素 + 后边的元素// 2 f[k] = f[k-1] + f[k-2];// 3 因为后面有f[k-2] 所以加足拆分 f[k -2] = f[k-3] + f[k-4];// 4 即在f[k-2] 的前面继续查找// 下次循环mid = f[k-1-2] -1k -= 2;}else { // 找到// 需要确定返回的是哪个下标if (mid <= high){return mid;}else {return high;}}}return -1; // 没有找到}}
