查找算法及应用

查找算法及应用

常用查找算法包括顺序/线性查找、二分/折半查找、插值查找、斐波那契查找。

  1. 线性查找

    逐一比对,发现有相同值返回即可。

  2. 二分查找

    前提是数组有序。如果数据是连续的情况下可以使用插值查找。

  3. 插值查找

    插值查找算法类似二分查找,不同的是插值查找每次从自适应中间位置处开始查找。数据比较连续情况下实用。将折半查找中的求mid索引的公式改进,low表示左边索引left,high表示右边索引right,key表示查找的值。
    m i d = l o w + h i g h 2 = l o w + 1 2 ( l o w + h i g h ) mid = \frac{low+high}{2} = low + \frac{1}{2}(low + high) mid=2low+high=low+21(low+high)

    m i d = l o w + k e y − a [ l o w ] a [ h i g h ] − a [ l o w ] ( h i g h − l o w ) mid = low + \frac{key - a[low]}{a[high] - a[low]}(high - low) mid=low+a[high]a[low]keya[low](highlow)

  4. 斐波那契查找

    黄金分割点是指把一条线段分成两段,使其中一部分与全长之比等于另一部分与这一部分之比,取其前三位数字的近似值是0.618。斐波那契数列的两个相邻数的比例,无线接近黄金分割值0.618。斐波那契查找原理是改变中间节点mid的位置,mid=low+F(k-1)-1,其中F代表斐波那契数列。

/**
 * 抽象父类
 */
public abstract class SearchParent {
    protected static int[] arr = {1, 8, 10, 89, 1000, 1000, 1000, 1234};

    //在数组中查找指定值的位置
    abstract int search(int[] arr, int val);
}
/**
 * 线性查找
 */
public class SimpleSearch extends SearchParent {
    
    public static void main(String[] args) {
        System.out.println("89的下标是:" + new SimpleSearch().search(arr, 89));
    }
    
    @Override
    int search(int[] arr, int val) {
    	for (int i=0;i<arr.length;i++){
            if (val == arr[i]) {
                return i;
            }
        }    
        return -1;
    }    
}
import java.util.ArrayList;
import java.util.List;

/**
 * 折半查找
 */
public class BinarySearch extends SearchParent {
    public static void main(String[] args) {
        integerOverflow();
        System.out.println("The index of 1000 is " + new BinarySearch().search(arr, 1000));
    }

    @Override
    int search(int[] arr, int val) {
        int left = 0;
        int right = arr.length - 1;
        return binarySearch(arr, left, right, val);
    }

    //只能查找到第一个匹配的值
    private static int binarySearch(int[] arr, int left, int right, int val){
        int mid = (left + right)/2;
        int tmp = arr[mid];
        if(left > right){
            return -1;
        }

        if(val > tmp){
            return binarySearch(arr, mid+1, right, val);
        }else if(val < tmp){
            return binarySearch(arr, left, mid-1, val);
        }else{
            return mid;
        }
    }

    //折半查找非递归方式实现
    private static int binarySearch(int[] arr, int val){
        int left = 0;
        int right = arr.length - 1;
        while(left <= right){
            int mid = (left + right)/2;
            if(arr[mid] > val){
                right = mid - 1;
            }else if(arr[mid] < val){
                left = mid + 1;
            }else{
                return mid;
            }
        }

        return -1;
    }

    //查找所有符和的值
    private static List<Integer> binary2Search(int[] arr, int left, int right, int val){
        int mid = (left + right)/2;
        int tmp = arr[mid];
        if(left > right){
            return null;
        }

        if(val > tmp){
            return binary2Search(arr, mid + 1, right, val);
        }else if(val < tmp){
            return binary2Search(arr, left, mid-1, val);
        }else{
            List<Integer> result = new ArrayList<>(arr.length);
            int temp = mid-1;
            while(temp >= 0 && arr[temp] == val) {
                result.add(temp);
                temp--;
            }

            result.add(mid);
            temp = mid + 1;
            while(temp <= arr.length-1 && arr[temp] == val){
                result.add(temp);
                temp++;
            }

            return result;
        }
    }

    //获取中间索引可能会出现整数溢出
    private static void integerOverflow(){
        int l = 0;
        int r = Integer.MAX_VALUE - 1;
        int m = (l+r)/2;
        System.out.println(m);
        l = m + 1;
        //m = (l+r)/2; 会导致整数溢出
        //m = l + (r-l)/2; 除法效率较差
        m = (l+r)>>>1;
        System.out.println(m);
    }
}
/**
 * 插值查找
 */
public class InsertValSearch extends SearchParent {

    public static void main(String[] args) {
        System.out.println("1000的下标是:" + new InsertValSearch().search(arr, 1000));
    }

    @Override
    int search(int[] arr, int val) {
        int left = 0;
        int right = arr.length - 1;
        return insertSearchValue(arr, left, right, val);
    }

    private int insertSearchValue(int[] arr, int left, int right, int val) {
        if(left > right || val < arr[0] || val > arr[arr.length-1]){
            return -1;
        }

        int mid = left + (right-left)*(val-arr[left])/(arr[right]-arr[left]);
        int temp = arr[mid];
        if(val > temp){
            return insertSearchValue(arr, mid+1, right, val);
        }else if(val < temp){
            return insertSearchValue(arr, left, mid+1, val);
        }else{
            return mid;
        }
    }
}

import java.util.Arrays;

/**
 * 斐波那契查找
 */
public class FibonacciSearch extends SearchParent {

    private static final int MAXSIZE = 20;

    public static void main(String[] args) {
        System.out.println(new FibonacciSearch().search(arr, 1000));
    }

    //生成斐波那契数组
    private int[] fib(){
        int[] f = new int[MAXSIZE];
        f[0] = 1;
        f[1] = 1;
        for(int i=2;i<MAXSIZE;i++){
            f[i] = f[i-2] + f[i-2];
        }

        return f;
    }

    @Override
    int search(int[] arr, int val) {
        int low = 0;
        int high = arr.length - 1;
        int k = 0; //表示斐波那契分割值的下标
        int mid = 0;
        int[] f = fib(); //获取斐波那契分割数值的下标
        //找到数组长度对应的斐波那契数列中对应的元素F(n)的值
        while(high > f[k] - 1){
            k++;
        }

        int[] temp = Arrays.copyOf(arr, f[k]); //F(k)的值可能大于arr的长度,不足部分使用0填充
        //使用数组的最后一个数填充
        for(int i=high+1;i<temp.length;i++){
            temp[i] = arr[high];
        }

        while(low <= high){
            mid = low + f[k-1] - 1;
            if(val < temp[mid]){
                //全部元素=前面元素+后面元素,f[k]=f[k-1]+f[k-2]
                //因为前面有f[k-1]个元素,所以可以继续拆分f[k-1]=f[k-2]+f[k-3]
                high = mid - 1;
                k-=1;
            }else if(val > temp[mid]){
                low = mid + 1;
                //因为后面有f[k-2]个元素,所以可以继续拆分
                k-=2;
            }else{
                if(mid <= high){ //如果是原查找表中的元素
                    return mid;
                }else{
                    return high; //如果是填充值
                }
            }
        }

        return -1;
    }
}

你可能感兴趣的:(算法与数据结构,算法,开发语言)