在java中,咱们常见的查找有四种java
咱们将一一介绍着四种查找方式的思想以及程序的实现。算法
顺序查找 的查找过程为:从数组的第一个元素开始,逐个将要查找的关键字和数组中的元素进行比较,若存在相等,则返回对应的下标。反之,若至最后一个元素,其关键字和元素都不相等,则代表数组中不存在要查找的数,查找不成功数组
举例说明:
有一个数列: {1,8, 10, 89, 1000, 1234} ,判断数列中是否包含数字8;要求: 若是找到了,就提示找到,并给出下标值。优化
public class seqSearch { public static void main(String[] args) { //定义数组arr int[] arr = {1,8, 10, 89, 1000, 1234}; //定义下标 int index = seqSearch(arr, 8); //当下标为-1时,说明要查找的数不存在 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; } } //由于下标数值老是>= 0,因此,当要查找的数不存在时,返回-1便可 return -1; } }
扩展 : 假如咱们要查的数在数组中存在两个及两个以上,咱们怎么把全部知足条件的元素下标输出呢?
思考: 这个问题的难点在于咱们并不知道数组有多大,以及要查的数到底有多少个,因此没法新建一个新的数组来保存知足条件的下标。因此咱们使用ArrayList 来存储保存知足条件的下标。设计
import java.util.ArrayList; public class seqSearch { public static void main(String[] args) { int[] arr = {1, 9, 11, 11, 52, 65, -4}; ArrayList<Integer> arrayList = seqSearch(arr, 11); //当没有查找到时,则arrayList中则没有下标值 System.out.println("arrayList:" + arrayList); } public static ArrayList<Integer> seqSearch(int[] arr, int value) { ArrayList<Integer> arrayList = new ArrayList<Integer>(); for (int i = 0; i < arr.length; i++) { if (arr[i] == value ) { //将知足条件的下标放入arrayList中 arrayList.add(i); } } return arrayList; } }
二分查找的查找过程:先肯定待查找记录所在的范围(区间),而后逐步缩小范围直到找到或找不到该记录为止。
接下来咱们将对这个查找过程进行分析和代码实现:
思路:3d
举例说明: 有一个数列{1, 2, 3, 4, 5, 6, 7, 8, 9},判断数列中是否包含数字8;要求: 若是找到了,就提示找到,并给出下标值。code
public class BinarySearch { public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; //注意: right 最大值 取不到 arr.length, 最大是arr.length - 1 int resIndex = binarySearch(arr,0, arr.length - 1,8); System.out.println("resIndex: " + resIndex); } //二分查找 /** * @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; } } }
扩展 : 假如咱们要查的数在数组中存在两个及两个以上,咱们怎么把全部知足条件的元素下标输出呢?
思考: 这个问题的难点在于咱们并不知道数组有多大,以及要查的数到底有多少个,因此没法新建一个新的数组来保存知足条件的下标。因此咱们使用ArrayList 来存储保存知足条件的下标。blog
import java.util.ArrayList; /** * 问题: * {1, 2, 3, 4, 5, 6, 7, 8, 8, 9} 当一个有序数组中,有多个相同的数值时, * 如何将全部的数值都查找到,好比这里的 8. */ public class BinarySearch2 { public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 8, 9}; ArrayList<Integer> resIndexList = binarySearch2(arr, 0, arr.length - 1, 8); System.out.println("resIndexList = " + resIndexList); } public static ArrayList 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 索引值的左边扫描,将全部等于 8 的元素的下标,加入到集合ArrayList // 3. 向mid 索引值的右边扫描,将全部等于 8 的元素的下标,加入到集合ArrayList // 4. 将ArrayList 返回 ArrayList<Integer> resIndexList = new ArrayList<Integer>(); //向mid 索引值的左边扫描,将全部等于 8 的元素的下标,加入到集合ArrayList int temp = mid - 1; while (true) { if (temp < 0 || arr[temp] != findval) { break; } //不然,就temp 放入resIndexList resIndexList.add(temp); //temp 左移 temp--; } //将中间的mid 下标放入 resIndexList.add(mid); //向mid 索引值的右边扫描,将全部等于 8 的元素的下标,加入到集合ArrayList temp = mid + 1; while (true) { if (temp > arr.length - 1 || arr[temp] != findval) { break; } //不然,就temp 放入resIndexList resIndexList.add(temp); //temp 右移 temp++; } return resIndexList; } } }
插入查找实际上是从二分查找 优化而来,改变了查找的规则,从而实现快速查找。插值查找算法的mid 是自适应的,而二分查找的 mid 老是 序列的中间值,插入查找的mid = low + (high - low) * (key - arr[low]) / (arr[high] - arr[low]) 。这里的low对应二分查找的left,right对应于二分查找的right,key就是咱们前面说的findVal
递归
思路同二分查找相同
举例说明: 插值查找算法 查找数组中数值为8的元素下标索引
import java.util.ArrayList; import java.util.Arrays; public class InsertValueSearch { //count 表示递归次数 private static int count = 0; public static void main(String[] args) { //定义一个有100个元素的数组 int arr[] = new int[100]; for (int i = 0; i < arr.length; i++) { arr[i] = i; } //将数组转成字符串输出 System.out.println(Arrays.toString(arr)); ArrayList<Integer> resArrayList = insertValueSearch(arr, 0, arr.length - 1, 8); System.out.println("resArrayList = " + resArrayList); System.out.println("递归次数:" + count); } //插值查找 public static ArrayList insertValueSearch(int[] arr, int left, int right, int findVal) { count++; //若是left > right,则说明已经递归完数组中全部的数字, 退出 //findval < arr[0] || findval > arr[arr.length - 1] 必需要有,由于当findVal很是大时,会致使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, left, mid - 1, findVal); } else if (findVal > midVal) { //向右递归 return insertValueSearch(arr, mid + 1, right, findVal); } else { ArrayList<Integer> resArrayList = new ArrayList<Integer>(); //往mid 左边扫描,将全部等于 8 的元素的下标,加入到集合ArrayList int temp = mid - 1; while (true) { //temp < 0 则意味着,mid在数组的最左边 if (temp < 0 || arr[temp] != findVal) { break; } resArrayList.add(temp); //左移一位 temp--; } //将中间的mid 下标放入 resArrayList.add(mid); //往mid 右边扫描,将全部等于 8 的元素的下标,加入到集合ArrayList temp = mid + 1; while (true) { //temp > arr.length - 1 则意味着,mid在数组的最右边 if (temp > arr.length - 1 || arr[temp] != findVal) { break; } resArrayList.add(temp); //右移 temp++; } return resArrayList; } } }
插值算法的注意事项:
PS:关于插值查找算法的mid值为何要这么求,我也不是很清楚。不过我认为若是你不是专门作算法的,那我们知道应该在什么时候何地使用这种算法就能够了。
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,89)); } //由于后面咱们mid = low + F(k-1) - 1,须要使用到斐波拉契数列,所以咱们须要先获取一个斐波那契数列 //非递归方法获得斐波那契数列 public static int[] fib() { 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; int mid = 0; //获取到斐波那契数列 int f[] = fib(); //获取到斐波那契分割数值的下标 while (high > f[k] - 1) { k++; } //由于f[k] 值可能大于咱们数组a 的长度,所以咱们须要使用Array类,构造一个新的数组,并指向a //不足的部分会使用0填充 int[] temp = Arrays.copyOf(a, f[k]); //实际上须要使用a数组最后的数填充temp /** * 举例说明: * int[] arr = {1, 8, 10, 89, 1000, 1234}; * int[] temp = Arrays.copyOf(a, f[k]); * 当 数组a 的长度 小于 f[k] 的长度, 咱们要将a 的值赋值给数组temp ,可是 * 数组temp 的长度是和f[k] 相同的,因此 数组temp 中没有被赋值的部分 填充为0; * 又由于,若是数组temp 中没有被赋值的部分 * */ for (int i = high + 1; i < temp.length; i++) { temp[i] = a[high]; } //使用while来循环处理,找到咱们的数key while (low <= high) { //只要这个条件知足,就能够继续寻找 //获得mid 中间值 的规则 不同 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] -1 */ //重点!!!!难点!!! k--; } 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] 的前面 进行查找 k -= 2 5. 即下次循环 mid = f[k - 1 - 2] - 1 */ //重点!!!!难点!!! k -=2; } else { //找到了 //须要肯定返回的是哪一个下标 if (mid <= high) { return mid; } else { return high; } } } return -1; } }
以上全部程序都在IDEA中运行过,没有任何问题。谢谢你们!共勉!