查找算法介绍,顺序查找,二分(折半)查找,插值查找,斐波那契查找,二叉树查找
一、在java中常用的四种查找方法:
1、顺序(线性)查找
2、二分查找、折半查找
3、插值查找
4、斐波那契查找
二、简单实例
1、顺序(线性)查找,依次查找,不要求数列的排序
/** * 线性查找,按照顺序依次查找(不要求数列有序) */ public class SeqSearch { public static void main(String[] args) { int[] num = {2, 4, 8, 12, 34, 1}; int target = 8; int index = -1; for (int i = 0; i < num.length; i++) { if (num[i] == target) { System.out.println(i); index = i; break; } } if (index == -1) { System.out.println("没有找到!"); } } }
2、二分查找、折半查找
进行一般的二分查找实例以及当这个查找的有序数组有多个相等的数时的改善
import java.util.ArrayList; import java.util.List; /** * 二分查找 */ public class BinarySearch { public static void main(String[] args) { int[] arr = { 2, 5, 56, 78, 90, 90, 90, 345, 568}; new BinarySearch().binarySearch(arr, 90, 0, arr.length - 1); ArrayList<Integer> integers = new BinarySearch().binarySearch1(arr, 90, 0, arr.length - 1); System.out.println(integers); } public void binarySearch(int[] arr, int num, int left, int right) { if (left > right) { System.out.println("没有找到~"); return; } int mid = (left + right) / 2; if (num == arr[mid]) { System.out.println("找到" + num + "对应的下标" + mid); return; } if (num < arr[mid]) { binarySearch(arr, num, left, mid - 1); } if (num > arr[mid]) { binarySearch(arr, num, mid + 1, right); } } public ArrayList<Integer> binarySearch1(int[] arr, int num, int left, int right) { if (left > right) { System.out.println("没有找到~"); return new ArrayList<>(); } int mid = (left + right) / 2; if (num < arr[mid]) { return binarySearch1(arr, num, left, mid - 1); } if (num > arr[mid]) { return binarySearch1(arr, num, mid + 1, right); } else { //找到需要找的数先不退出,对该位置的左边和右边进行查找看是否有符合的数 ArrayList<Integer> resList = new ArrayList<>(); int temp = mid - 1; while (true) { //如果已经到达最左边就退出循环,如果该位置的上一个不是要找的数就退出循环 // 反之就不是连续相等的,直接退出循环 if (temp < 0 || arr[temp] != num) { break; } resList.add(temp); temp -= 1; } //将二分查找找到的对应的位置放入到列表中 resList.add(mid); temp = mid + 1; while (true) { //如果已经到达最右边就退出循环,如果该位置的下一个不是要找的数就退出循环 // 反之就不是连续相等的,直接退出循环 if (temp > arr.length - 1 || arr[temp] != num) { break; } resList.add(temp); temp += 1; } return resList; } } }
3、插值查找
1)插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid处开始查
找。
2)将折半查找中的求mid索引的公式, low表示左边(left)索引弧high表示右边()right索引.
4)举例说明插值查找算法1-100的数组
注意:插值查找注意事项
(1)对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找,速度较快
(2)关键字分布不均匀,该方法不一定比折半查找好(关键字分布均匀很重要)
package com.zjl.search; public class InsertValueSearch { public static void main(String[] args) { int[] arr = { 2, 5, 56, 78, 90, 90, 90, 345, 568}; int i = InsertValueSearch.insertValueSearch(arr, 0, arr.length - 1, 78); System.out.println(i); } public static int insertValueSearch(int[] arr, int left, int right, int findNum) { if (left > right || findNum < arr[left] || findNum > arr[right]) { return -1; } System.out.println("查找次数"); //首先可以计算数组中所查找的元素占所有元素的比例的期望值(因为插值查找的数组也是有序才能定位) // int mid = left + (right - left)* (findVal - arr[left])/ (arr[right]- arr[left]) int mid = left + (right - left) * (findNum - arr[left]) / (arr[right] - arr[left]); if (findNum < arr[mid]) { return insertValueSearch(arr, left, mid - 1, findNum); } if (findNum > arr[mid]) { return insertValueSearch(arr, mid + 1, right, findNum); } else { return mid; } } }
4、斐波那契查找
基本介绍:斐波那契(黄金分割法)查找基本介绍
1)黄金分割点是指把一条线段分割为两部分,使其中一部分与全
长之比等于另一部分与这部分之比。取其前三位数字的近似值是0.618。由于按此比例设计的造型十分美丽,因此称为黄金分割,也称为中外比。这是一个神奇的数字,会带来意向不大的效果。
2)斐波那契数列{1,1,2,3,5,8,13,21,34,55}发现斐波那契数
列的两个相邻数的比例,无限接近黄金分割值0.618
斐波那契(黄金分割法)原理:斐波那契查找原理与前两种相似,仅仅改变了中间结点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分割点附近,即mid=low+F(k-1)-1(F代表斐波那契数列),如卞图所示
对F(k-1)-1的理解:
1)由斐波那契数列F[K]=F[Ik-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=low+F(k-1)-1
2)类似的,每一子段也可以用相同的方式分割
3)但顺序表长度n不一定刚好等于FK-1,所以需要将原来的顺序表长度n增加至F[K-1。这里的k值只
要能使得F[K-1恰好大于或等于n郎可,由以下代码得到,顺序表长度增加后,新增的位置(从n+1到F[k-1位置),都赋为n位置的值即可。
代码实例
package com.zjl.search; import java.util.Arrays; public class FibonacciSearch { public static void main(String[] args) { int[] arr = {2, 5, 56, 78, 90, 90, 90, 345, 568}; System.out.println(new FibonacciSearch().fibonacciSearch(arr, 2)); } public int[] f() { int[] F = new int[20]; 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 int fibonacciSearch(int[] arr, int key) { int low = 0; int high = arr.length - 1; int k = 0;//表示斐波那契的下标 int mid = 0;//用来存放mid值 int[] f = f(); while (high > f[k] - 1) { k++; } ////因为f[k]值可能大于 a 的长度,因此我们需要使用Arrays类,构造一个新的数组,并指向a[] //不足的用0填充 int[] temp = Arrays.copyOf(arr, f[k]); //实际需求后面不能为0,要将后面设置为数组最大的那一个数 for (int i = arr.length; i < temp.length; i++) { temp[i] = arr[high]; } while (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 = low+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 = low+f[k - 1 - 2] - 1 k -= 2; } else { //需要确定,返回的是哪个下标 if (mid <= high) { return mid; } else { return high; } } } return -1; } }
5、二叉树查找:
package com.zjl.search; public class BinaryTreeSearch { public static void main(String[] args) { //创建一个简单的二叉树 Node E = new Node('E'); Node F = new Node('F'); F.leftNode = E; Node A = new Node('A'); Node D = new Node('D'); D.leftNode = A; D.rightNode = F; Node H = new Node('H'); Node Z = new Node('Z'); Node m = new Node('M'); m.leftNode = H; m.rightNode = Z; Node G = new Node('G'); G.leftNode = D; G.rightNode = m; preSearch(G, 'H'); midSearch(G, 'H'); afterSearch(G, 'H'); } //前序查找 public static Node preSearch(Node node, char findVal) { if (node != null) { Node resNode = null; System.out.print(node.data+"->"); if (node.data == findVal) { System.out.println("找到该值~"); return node; } if (node.leftNode != null) { resNode = preSearch(node.leftNode, findVal); } //如果左边递归找到就不进行右边递归 if (resNode != null) { return resNode; } if (node.rightNode != null) { preSearch(node.rightNode,findVal); } } return null; } //中序查找 public static Node midSearch(Node node, char findVal) { if (node != null) { Node resNode = null; if (node.leftNode != null) { resNode = midSearch(node.leftNode, findVal); } //如果左子节点找到就退出 if (resNode != null) { return resNode; } System.out.print(node.data+"->"); if (node.data == findVal) { System.out.println("找到该值~"); resNode = node; return resNode; } //找到就退出,不再进行下面的遍历 if (node.rightNode != null) { resNode = midSearch(node.rightNode,findVal); } if (resNode != null) { return resNode; } } return null; } //后序查找 public static Node afterSearch(Node node, char findVal) { if (node != null) { Node resNode = null; if (node.leftNode != null) { resNode = afterSearch(node.leftNode, findVal); } //左子节点找到就不进行下面的遍历 if (resNode != null) { return resNode; } if (node.rightNode != null) { resNode = afterSearch(node.rightNode, findVal); } //右子节点找到就不再回头比较父节点 if (resNode != null) { return resNode; } System.out.print(node.data+"->"); if (node.data == findVal) { System.out.println("找到该值~"); resNode = node; } return resNode; } return null; } }