一、概述
1、介绍
静态查找:数据集合稳定,不会添加,删除元素的查找操作。
动态查找:数据集合在查找的过程中会添加,删除元素的查找操作。
2、查找方式
静态查找:不妨使用线性表结构组织数据,可以使用顺序查找算法;若对关键词排序,可以使用折半查找算法或斐波那契查找算法。
动态查找:可以使用二叉排序树的查找技术;还可以使用散列表结构来解决查找问题。
无序查找:顺序查找O(n)。
有序查找:折半查找O(log2n)、插值查找O(log2n)、斐波那契查找O(log2n)。三种有序表的查找本质上是分割点的选择不同,各有优劣,实际开发可根据数据的特点综合考虑再做决定。
3、平均查找长度(Average Search Length,ASL)
需和指定key进行比较的关键字的个数的期望值,成为查找算法在查找成功时的平均查找长度。
对于含有 n 个数据元素的查找表,查找成功的平均查找长度为:ASL = Pi*Ci的和。
Pi:查找表中第 i 个数据元素的概率。
Ci:找到第 i 个数据元素时已经比较过的次数。
二、顺序(线性)查找
1、思想
从第一个(或者最后一个)记录开始,逐个进行记录的关键字和给定值的比较,若某个记录的关键字和给定值相等,则查找成功。否则查找不成功。
2、代码
1 // 顺序查找.O(2n) 2 public static int orderSearch(int[] arr, int key) { 3 for (int i = 0; i < arr.length; i++) { 4 if (arr[i] == key) { 5 return i; 6 } 7 } 8 return -1; 9 }
三、折半(二分)查找
1、思想
取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录左半区继续查找;否则,在右半区查找。不断重复,直到查找成功或者查找失败为止。
2、代码
代码示例:返回单个
1 // 折半查找.O(log2n).方式一 2 public static int halfSearch(int[] arr, int key) { 3 int left = 0, right = arr.length - 1, mid; 4 5 while (left <= right) { 6 mid = (left + right) / 2; 7 8 if (key == arr[mid]) { 9 return mid; 10 } 11 12 if (key < arr[mid]) { 13 right = mid - 1; 14 } else { 15 left = mid + 1; 16 } 17 18 } 19 return -1; 20 } 21 22 // 折半查找.方式二 23 public static int halfSearch2(int[] arr, int key) { 24 int left = 0, right = arr.length - 1, mid; 25 mid = (left + right) / 2; 26 27 while (key != arr[mid]) { 28 if (key < arr[mid]) { 29 right = mid - 1; 30 } else { 31 left = mid + 1; 32 } 33 34 if (left > right) { 35 return -1; 36 } 37 mid = (left + right) / 2; 38 } 39 return mid; 40 } 41 42 // 折半查找.方式三.递归 43 public static int halfSearch(int[] arr, int left, int right, int key) { 44 if (left > right) { 45 return -1; 46 } 47 48 int mid = (left + right) / 2; 49 if (key == arr[mid]) { 50 return mid; 51 } 52 53 if (key < arr[mid]) { 54 return halfSearch(arr, left, mid - 1, key); 55 } else { 56 return halfSearch(arr, mid + 1, right, key); 57 } 58 }
代码示例:返回多个
1 // 折半查找.返回多个 {1, 8, 10, 89, 1000, 1000, 1234} 2 public static List<Integer> halfSearch2(int[] arr, int left, int right, int key) { 3 if (left > right) { 4 return new ArrayList<>(); 5 } 6 7 int mid = (left + right) / 2; 8 if (key == arr[mid]) { 9 List<Integer> result = new ArrayList<>(); 10 result.add(mid); 11 12 // 向mid左边扫描 13 int temp = mid - 1; 14 while (temp >= 0 && arr[temp] == key) { 15 result.add(temp); 16 temp--; 17 } 18 19 // 向mid右边扫描 20 temp = mid + 1; 21 while (temp < arr.length && arr[temp] == key) { 22 result.add(temp); 23 temp++; 24 } 25 26 return result; 27 } 28 29 if (key < arr[mid]) { 30 return halfSearch2(arr, left, mid - 1, key); 31 } else { 32 return halfSearch2(arr, mid + 1, right, key); 33 } 34 }
四、插值(按比例)查找
1、思想
折半查找的改进算法,将折半查找的比例参数1/2改进了,根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。
适用于数据量较大,关键字分布较均匀。关键字分布不均匀时,该方法不一定比折半查找好。
2、代码
1 // 插值查找. 2 public static int insertSearch(int[] arr, int key) { 3 int left = 0, right = arr.length - 1, mid; 4 5 while (left <= right) { 6 // 按比例改进mid 7 mid = ((key - arr[left]) / (arr[right] - arr[left]) * (right - left)) + left; 8 9 if (key == arr[mid]) { 10 return mid; 11 } 12 13 if (key < arr[mid]) { 14 right = mid - 1; 15 } else { 16 left = mid + 1; 17 } 18 } 19 return -1; 20 } 21 22 // 插值查找.递归 23 public static int insertSearch(int[] arr, int left, int right, int key) { 24 if (left > right || key < arr[0] || key > arr[arr.length - 1]) { 25 return -1; 26 } 27 int mid = ((key - arr[left]) / (arr[right] - arr[left]) * (right - left)) + left; 28 29 if (key == arr[mid]) { 30 return mid; 31 } 32 33 if (key < arr[mid]) { 34 return insertSearch(arr, left, mid - 1, key); 35 } else { 36 return insertSearch(arr, mid + 1, right, key); 37 } 38 }
五、斐波那契查找(黄金分割法查找)
1、思想
折半查找的改进算法。优化 mid 的位置。
2、代码
1 // Fibonacci查找 2 public static int fibonacciSearch(int[] arr, int key) { 3 4 5 return key; 6 }
未完成。。。