查找算法

查找算法

1. 线性查找

线性查找(Order Search)是最简单的一种查找算法,直接从头到尾遍历,直至找到要查找的值为止。

1.1 代码实现

package com.algorithm;

/**
 * @author SnkrGao
 * @create 2023-04-20 19:52
 */
public class OrderSearch {
    public static void main(String[] args) {
        int[] nums = {1, 8, 10, 89, 1000, 1234};
        int searchValue = 89;
        int searchIndex = orderSearch(nums, searchValue);
        System.out.println("searchIndex=" + searchIndex);
    }

    public static int orderSearch(int[] nums, int searchValue) {
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == searchValue) {
                return i;
            }
        }
        return -1;
    }
}

2. 二分查找

二分查找又叫折半查找,即从中间数开始查找,根据比较结果选择折半的一边,再次折半继续进行查找。二分查找是一种效率较高的查找方法,但是其要求线性表中的记录必须按关键字有序排列(即前提是线性表必须有序),并且必须采用顺序存储

2.1 Binary Search设计思路(找到一个数就return)

  • 确定当前数组的下标mid = (left + right) / 2
  • 让要查找的数searchValue与nums[mid]进行比较
    • searchValue < nums[mid],说明待查找的数在mid左边向左递归查找
    • searchValue > nums[mid],说明待查找的数在mid右边向右递归查找
    • searchValue == nums[mid],说明找到该数,直接返回;
  • 注意递归终止条件:
    • 找到待查找数时结束递归
    • 递归查找完整个数组仍没有找到,也即当left > right时,结束递归
    • 此处应注意left == right时,仍有可能找到,即nums[left] == nums[right] == nums[mid]可能为待查找值。

2.3 代码实现

递归:

public static int binarySearch(int[] nums, int searchValue, int left, int right) {
    if (left > right) { // 递归终止条件
        return -1;
    }
    // 折半
    int mid = (left + right) / 2;
    int midValue = nums[mid];

    if (searchValue < midValue) {
        return binarySearch(nums, searchValue, left, mid - 1);
    } else if (searchValue > midValue) {
        return binarySearch(nums, searchValue, mid + 1, right);
    } else {
        return mid;
    }
}

迭代:

public static int binarySearch(int[] nums, int searchValue) {
    int left = 0;
    int right = nums.length - 1;
    while (left <= right) {
        int mid = (left + right) / 2;
        if (searchValue < nums[mid]) {
            right = mid - 1;
        } else if (searchValue > nums[mid]) {
            left = left + 1;
        } else {
            return mid;
        }
    }
    return -1;
}

2.4 Binary Search设计思路(找到所有满足条件的值)

  • 用一个List来接收所有满足条件的值
  • 向左递归和向右递归不需要修改,当找到第一个匹配的索引值mid时,不直接返回而是将下标mid添加到list中
    • mid索引的左侧继续扫描,将所有与searchValue相等的元素下标添加到list中;
    • mid索引的右侧继续扫描,将所有与searchValue相等的元素下标添加到list中。

2.5 代码实现

package com.algorithm;

import java.util.ArrayList;
import java.util.List;

/**
 * @author SnkrGao
 * @create 2023-04-20 20:11
 */
public class BinarySearch {
    public static void main(String[] args) {
        int[] nums = {1, 8, 10, 89, 1000, 1000, 1000, 1000, 1234};
        int searchValue = 1000;
        List<Integer> searchIndex = binarySearch(nums, searchValue, 0, nums.length - 1);
        System.out.println("searchIndex=" + searchIndex);
    }

    public static List<Integer> binarySearch(int[] nums, int searchValue, int left, int right) {
        if (left > right) { // 递归终止条件,说明递归完毕仍没有找到
            return new ArrayList<>();
        }

        // 折半
        int mid = (left + right) / 2;
        int midValue = nums[mid];

        if (searchValue < midValue) {
            // 向左递归查找
            return binarySearch(nums, searchValue, left, mid - 1);
        } else if (searchValue > midValue) {
            // 向右递归查找
            return binarySearch(nums, searchValue, mid + 1, right);
        } else { // 已经找到了第一个与searchValue相等的值,继续在其周围找其他相同的值
            List<Integer> searchIndexList = new ArrayList<>();

            // 由于二分查找的前提是有序,因此相同的值一定在第一个找到的nums[mid]的两侧
            // 继续向左扫描,但不折半
            int temp = mid - 1;
            while (temp >= 0 && nums[temp] == searchValue) {
                searchIndexList.add(temp);
                temp -= 1;
            }

            searchIndexList.add(mid);

            // 继续向右扫描
            temp = mid + 1;
            while (temp <= right && nums[temp] == searchValue) {
                searchIndexList.add(temp);
                temp += 1;
            }

            return searchIndexList;
        }
    }
}

3. 插值查找

  • 插值查找(Interpolation Search)类似于二分查找,不同的是插值查找每次从自适应mid处开始查找
  • 插值查找的mid值通过公式计算得来,mid = left + (right - left) × (searchValue - nums[left]) / (nums[right] - nums[left])
  • 该公式使得mid的变化更靠近searchValue,相当于间接减少了查找的次数;
  • 插值查找同样只适用于有序序列,另外还要求数据元素的关键字在线性表中均匀分布
  • 对于数据量大且关键字分布均匀有序序列来说,插值查找的速度较快
  • 对于分布不均匀的有序序列而言,插值查找并不一定比二分查找好。

3.1 代码实现

package com.algorithm;

import java.util.Arrays;

/**
 * @author SnkrGao
 * @create 2023-04-20 22:41
 */
public class InterpolationSearch {

    public static void main(String[] args) {
        int[] nums = new int[100];
        for (int i = 0; i < nums.length; i++) {
            nums[i] = i + 1;
        }
        System.out.println(Arrays.toString(nums));
        int searchValue = 87;
        int searchIndex = interpolationSearch(nums, searchValue, 0, nums.length - 1);
        System.out.println("searchIndex=" + searchIndex);
    }

    public static int interpolationSearch(int[] nums, int searchValue, int left, int right) {
        // 一定要添加下面的两个限制,否则根据mid的计算公式可能出现mid数组下标越界的问题
        if (left > right || searchValue < nums[0] || searchValue > nums[nums.length - 1]) {
            return -1;
        }

        int mid = left + (right - left) * (searchValue - nums[left]) / (nums[right - left]);
        int midValue = nums[mid];

        if (searchValue < midValue) {
            return interpolationSearch(nums, searchValue, left, mid - 1);
        } else if (searchValue > midValue) {
            return interpolationSearch(nums, searchValue, mid + 1, right);
        } else {
            return mid;
        }
    }
}
posted @ 2023-04-21 21:29  GaoakaJie  阅读(75)  评论(0)    收藏  举报