JAVA常见的查找算法

目录

1.线性查找

2.二分查找

3.插值查找

分块查找

斐波那契查找(了解)

哈希查找(了解)


1.线性查找

遍历整个数组或集合,逐一比对每个元素,直到找到目标元素或遍历完所有元素。

2.二分查找

适用于有序的数组或集合,通过反复将查找范围减半来定位目标元素。

 public static int Binary_Search(int [] arr,int number){
        int left=0;//数组左下标
        int right=arr.length-1;//数组右下标
        while (left<=right){
            int mid=(left+right)/2;//数组中间下标
            if(arr[mid]<number){//查找的数据在arr[mid]的右边
                left=mid+1;//舍去arr[mid]左边的数据
            } else if (arr[mid]>number) {//查找的数据在arr[mid]的左边
                right=mid-1;//舍去arr[mid]右边的数据
            }else {
                return mid;//查找成功,返回查找数据的下标
            }


        }
        return -1;//查找失败,返回-1
    }

二分查找的优点:

高效性:对于有序数组,二分查找的时间复杂度为O(log n),比线性查找的O(n)更高效。
简洁性:算法实现相对简单,易于理解和编码。

二分查找的缺点:

依赖排序:二分查找要求数组必须是有序的,如果数组未排序,需要先进行排序,这会增加额外的时间复杂度。
不适用于动态数据:对于频繁插入、删除操作的数据集,维护有序性成本较高,二分查找可能不是最佳选择。

3.插值查找

改进的二分查找,使用插值公式估计目标元素的位置,适用于均匀分布的数组。

适用条件:有序性

                  均匀分布或近似均匀分布

public static int Binary_Search_Improve(int [] arr,int number){
        int left=0;//数组左下标
        int right=arr.length-1;//数组右下标
        while (left<=right){
            int mid=(number-arr[left])*(right-left)/(arr[right]-arr[left])+left;//int mid=(left+right)/2;//数组中间下标
            if(arr[mid]<number){//查找的数据在arr[mid]的右边
                left=mid+1;//舍去arr[mid]左边的数据
            } else if (arr[mid]>number) {//查找的数据在arr[mid]的左边
                right=mid-1;//舍去arr[mid]右边的数据
            }else {
                return mid;//查找成功,返回查找数据的下标
            }


        }
        return -1;//查找失败,返回-1
    }

常见BUG:

数组无序:插值查找要求数组必须是有序的,如果数组无序,插值查找的结果将无法保证正确。

数组范围问题:当查找的值小于数组中的最小值或大于数组中的最大值时,插值查找的计算公式可能会导致数组越界,从而引发错误。

计算精度问题:在计算mid位置时,由于涉及到浮点数运算,可能会存在精度问题。虽然这通常不会影响最终结果(因为mid最终会被取整为数组索引),但在某些极端情况下,精度问题可能导致查找路径偏离最优。

递归深度问题:如果数组很大且目标值远离数组中间位置,插值查找可能会进行多次递归调用,这可能导致栈溢出错误,尽管这种情况相对较少见。

二分查找VS差值查找

数据分配均匀

案例:


class Main {
    public static void main(String[] args) {
        int [] arr=new int[1000000];
       for (int i = 0; i <arr.length ; i++) {
            arr[i]=i;
        }
        long begain1=System.nanoTime();//获取开始时间
       Binary_Search(arr,2);//运行程序
        long end1=System.nanoTime();//结束时间
        long begain2=System.nanoTime();
       Binary_Search_Improve(arr,2);
        long end2=System.nanoTime();
        long time1=end1-begain1;//程序执行时间
        long time2=end2-begain2;
       System.out.println("二分查找法消耗时间:"+time1);
       System.out.println("插值查找法消耗时间:"+time2);


    }
    public static int Binary_Search_Improve(int [] arr,int number){
        int left=0;//数组左下标
        int right=arr.length-1;//数组右下标
        while (left<=right){
            int mid=(number-arr[left])*(right-left)/(arr[right]-arr[left])+left;//int mid=(left+right)/2;//数组中间下标
            if(arr[mid]<number){//查找的数据在arr[mid]的右边
                left=mid+1;//舍去arr[mid]左边的数据
            } else if (arr[mid]>number) {//查找的数据在arr[mid]的左边
                right=mid-1;//舍去arr[mid]右边的数据
            }else {
                return mid;//查找成功,返回查找数据的下标
            }


        }
        return -1;//查找失败,返回-1
    }
    public static int Binary_Search(int [] arr,int number){
        int left=0;//数组左下标
        int right=arr.length-1;//数组右下标
        while (left<=right){
           int mid=(left+right)/2;//数组中间下标
            if(arr[mid]<number){//查找的数据在arr[mid]的右边
                left=mid+1;//舍去arr[mid]左边的数据
            } else if (arr[mid]>number) {//查找的数据在arr[mid]的左边
                right=mid-1;//舍去arr[mid]右边的数据
            }else {
                return mid;//查找成功,返回查找数据的下标
            }


        }
        return -1;//查找失败,返回-1
    }

}

运行结果: 

分块查找

分块查找通过将查找表分为若干个子块(或称为索引表),每个子块内部元素可以无序,但子块之间是有序的(即第一个子块中的最大元素小于第二个子块中的最小元素,以此类推),并且建立一个索引表,索引表中的每个元素包含对应子块中的最大(或最小)关键字和该子块在查找表中的起始位置。查找过程分为两步:首先,在索引表中进行查找,确定待查关键字可能存在的子块;然后,在已确定的块中采用顺序查找,找到对应的节点。

import java.util.ArrayList;
class Main {
    public static void main(String[] args) {
        ArrayList<Block> list=new ArrayList<Block>();
        int [] arr={7,5,1,12,9,16,14,29,20,25};
        Block block1=new Block(7,0,2);
        list.add(block1);
        Block block2=new Block(14,3,6);
        list.add(block2);
        Block block3=new Block(29,7,9);
        list.add(block3);
        System.out.println(Block_Search(list,28,arr));



    }
    public static int Block_Search(ArrayList<Block> list,int number,int [] arr){
        for (int i = 0; i < list.size(); i++) {
            Block block=list.get(i);//取出分块
            //分块中的数据最大值有小于number中值
            if(number<=block.getMax()){
                //在分快中查找数据
                return Search_in_Block(block,number,arr);
            }
        }
        return -1;
    }
    public static int Search_in_Block(Block block,int number,int [] arr){
        for (int i = block.getBegin(); i <=block.getEnd() ; i++) {
            //分块中存在要查找的数
            if(arr[i]==number){
                return i;
            }
        }
        //分块中不存在要查找的数
        return -1;
    }

}
class Block{
    private int max;//最大值
    private int begin;//开始下标
    private int end;//结束下标

    public Block() {
    }

    public Block(int max, int begin, int end) {
        this.max = max;
        this.begin = begin;
        this.end = end;
    }

    public int getMax() {
        return max;
    }

    public void setMax(int max) {
        this.max = max;
    }

    public int getBegin() {
        return begin;
    }

    public void setBegin(int begin) {
        this.begin = begin;
    }

    public int getEnd() {
        return end;
    }

    public void setEnd(int end) {
        this.end = end;
    }
}

分块原则: 块数数量一般等于数字的个数开根号。比如:16个数字一般分为4块左右

斐波那契查找(了解)

使用斐波那契数列确定查找范围,通过缩小查找区间来定位目标元素,适用于分块或近似均匀分布的数组。

public static int fibonacciSearch(int[] arr, int key) {
        int low = 0;
        int high = arr.length - 1;
        int k = 0; // 斐波那契数列的下标
        int[] fib = generateFibonacciArr(arr.length);

        // 找到大于等于数组长度的最小斐波那契数列元素
        while (arr.length > fib[k] - 1) {
            k++;
        }

        // 将数组扩展到斐波那契数列元素的长度
        int[] temp = Arrays.copyOf(arr, fib[k] - 1);

        // 将扩展部分用数组最后一个元素填充
        for (int i = arr.length; i < temp.length; i++) {
            temp[i] = arr[arr.length - 1];
        }

        // 查找过程
        while (low <= high) {
            int mid = low + fib[k - 1] - 1;
            if (key < temp[mid]) {
                high = mid - 1;
                k -= 1;
            } else if (key > temp[mid]) {
                low = mid + 1;
                k -= 2;
            } else {
                return Math.min(mid, arr.length - 1);
            }
        }

        return -1;
    }

    // 生成斐波那契数列
    private static int[] generateFibonacciArr(int n) {
        int[] fib = new int[n];
        fib[0] = 1;
        fib[1] = 1;
        for (int i = 2; i < n; i++) {
            fib[i] = fib[i - 1] + fib[i - 2];
        }
        return fib;
    }

哈希查找(了解)

利用哈希表实现快速查找,通过哈希函数将元素映射到特定位置,适用于需要高效查找的场景。

// 哈希查找函数
    public static int hashSearch(int[] array, int target) {
        // 创建一个HashMap用于存储数组元素及其对应的索引
        HashMap<Integer, Integer> map = new HashMap<>();

        // 遍历数组,将元素及其索引放入HashMap中
        for (int i = 0; i < array.length; i++) {
            map.put(array[i], i);
        }

        // 查找目标值是否在HashMap中存在
        if (map.containsKey(target)) {
            // 如果存在,返回目标值在数组中的索引
            return map.get(target);
        } else {
            // 如果不存在,返回-1表示未找到
            return -1;
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值