查找

谁说我不能喝 提交于 2019-11-30 13:35:30

顺序查找

  顺序查找是指将序列从头开始遍历,直到找到指定的元素为止。

  在一个元素为n的序列中查找不存在的元素,需要比较n次。顺序查找虽然效率低下,但是却适用于任何序列。

  顺序查找的代码如下:

1 public static int seqSearch(int[] arr, int val) {
2     for (int i = 0; i < arr.length; i++) {
3         if (arr[i] == val) return i;
4     }
5     return -1;
6 }
seqSearch

  例如,在序列(1, 2, 3, 4, 5, 6, 7, 8, 9)中查找元素7:从1开始比较,直到找到了7所在位标并返回。

  顺序查找一共比较了7次。

二分查找

  二分查找是指取有序序列中间的元素作为枢轴,枢轴两边被分割为两个子序列,一边比枢轴大,另一边比枢轴小。这样,查找的元素要么为枢轴,要么在其中一个子序列中。每次递归查找时,可以将区间规模大致缩减一半,从而大大加快查找效率。

  在一个元素为n的序列中查找不存在的元素,需要比较log2n次。二分查找只适用于有序序列。

  二分查找的代码如下:

1 public static int binarySearch(int[] arr, int low, int high, int val) {
2     if (low > high) return -1;
3     int mid = (low + high) / 2;
4     if (arr[mid] == val) return mid;
5     if (arr[mid] > val)   // 降序则为arr[mid] < val
6         return binarySearch(arr, low, mid - 1, val);
7     return binarySearch(arr, mid + 1, high, val);
8 }
binarySearch

  例如,在序列(1, 2, 3, 4, 5, 6, 7, 8, 9)中查找元素7:

    a. 找到序列最中间的元素5,5 < 7,需要对右子序列递归查找。

    b. 找到右子序列最中间的元素7,7 = 7,找到元素。

  二分查找一共比较了2次。

插值查找

  插值查找与二分查找类似。二分查找是以序列中间的元素作为枢轴,即

    

  而插值查找获取枢轴的公式为:

    

  其中,low和high分别是当前序列第一个元素的位标和最后一个元素的位标。

  与二分查找一样,插值查找只适用于有序序列。有序序列越接近等差序列,插值查找的效率越高。

  插值查找的代码如下:

1 public static int insertValueSearch(int[] arr, int low, int high, int val) {
2     // arr[low] > val和arr[high] < val是为了防止val数值过大,导致算出的mid越界
3     if (low > high || arr[low] > val || arr[high] < val) return -1;
4     int mid = low + (high - low) * (val - arr[low]) / (arr[high] - arr[low]);
5     if (arr[mid] == val) return mid;
6     if (arr[mid] > val)   // 降序则为arr[mid] < val
7         return insertValueSearch(arr, low, mid - 1, val);
8     return insertValueSearch(arr, mid + 1, high, val);
9 }
insertValueSearch

  例如,在序列(1, 2, 3, 4, 5, 6, 7, 8, 9)中查找元素7:直接代入计算枢轴的值为pivot = 0 + [(7 - 1) / (9 - 1)] * (8 - 0) = 6,而arr[6] = 7。

  插值查找一共比较了1次。

斐波那契查找

  斐波那契数列的特点为:f(n) = f(n - 1) + f(n - 2)。将该式变化后得到:f(n) - 1 = [f(n - 1) - 1] + [f(n - 2) - 1] + 1。

  斐波那契查找也与二分查找类似,其获取枢轴的公式为:

    

  也就是说,枢轴将有序序列分割为:

    

  斐波那契查找只适用于有序序列,而且在查找前需要保证序列长度为f(k) - 1(如果长度不为f(k) - 1则需要扩容,其扩容的空间使用原序列最后一个元素填充)。

  斐波那契查找的代码如下:

 1 public static int fibonacciSearch(int[] arr, int val) {
 2     int low = 0, high = arr.length - 1, mid;
 3     // 找到第一个满足f(k) - 1 >= arr.length的k
 4     int k = 1;
 5     while (f.get(k) - 1 < arr.length) k++;
 6     // 扩容
 7     int[] temp = Arrays.copyOf(arr, f.get(k) - 1);
 8     for (int i = arr.length; i < temp.length; i++) temp[i] = arr[arr.length - 1];
 9     while (low <= high) {
10         mid = low + f.get(k - 1) - 1;
11         if (val < temp[mid]) {   // 降序则为val > temp[mid]
12             high = mid - 1;
13             k--;
14         } else if (val > temp[mid]) {   // 降序则为val < temp[mid]
15             low = mid + 1;
16             k -= 2;
17         } else {
18             if (mid <= high) return mid;
19             return high;
20         }
21     }
22     return -1;
23 }
fibonacciSearch

  例如,在序列(1, 2, 3, 4, 5, 6, 7, 8, 9)中查找元素7:

    a. 原序列长度为9 < f(6) - 1 = 12,所以需要扩容到长度为12,且使用元素9填充,序列变为(1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9)。此时,k = 6。

    b. 枢轴pivot = 0 + f(6 - 1) - 1 = 7,arr[pivot] =  8 > 7,所以7位于左子序列(0, 6)中。此时,k = 5。

    c. 枢轴pivot = 0 + f(5 - 1) - 1 = 4,arr[pivot] = 5 < 7,所以7位于右子序列(5, 6)中。此时,k = 3。

    d. 枢轴pivot = 5 + f(3 - 1) - 1 = 6,arr[pivot] = 7,找到指定元素的位标。

  斐波那契查找一共比较了3次。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!