数据结构与算法(九)——查找

一、概述

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  }

 

未完成。。。

posted @ 2021-02-09 00:45  Craftsman-L  阅读(101)  评论(0编辑  收藏  举报