Java数据结构与算法——线性查找 & 二分查找 & 插值查找

Posted 张起灵-小哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java数据结构与算法——线性查找 & 二分查找 & 插值查找相关的知识,希望对你有一定的参考价值。

1.线性查找

有一个数列: 1,8, 10, 89, 1000, 1234 ,判断数列中是否包含此名称【顺序查找】 要求: 如果找到了,就提示找到,并给出下标值。

package com.szh.search;

/**
 * 线性查找
 */
public class SeqSearch 

    //这里我们实现的线性查找是找到一个满足条件的值,就返回
    private static int seqSearch(int[] arr, int value) 
        //线性查找是逐一比对,发现有相同值,就返回下标
        for (int i = 0; i < arr.length; i++) 
            if (arr[i] == value) 
                return i;
            
        
        return -1;
    

    public static void main(String[] args) 
        int[] arr =  1, 9, 11, -1, 34, 89 ; //无序数组
        int index = seqSearch(arr, 34);
        if (index == -1) 
            System.out.println("没有找到这个值");
         else 
            System.out.println("找到了这个值,对应下标为:" + index);
        
    


2.二分查找

二分查找:请对一个有序数组进行二分查找 1,8, 10, 89, 1000, 1234 ,输入一个数看看该数组是否存在此数,并且求出下标,如果没有就提示"没有这个数"。

课后思考题: 1,8, 10, 89, 1000, 1000,1234 当一个有序数组中,有多个相同的数值时,如何将所有的数值都查找到,比如这里的 1000。

package com.szh.search;

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

/**
 * 二分查找
 */
public class BinarySearch 

    /**
     * 二分查找算法
     * @param arr 数组
     * @param left 左边的索引
     * @param right 右边的索引
     * @param findVal 要查找的值
     * @return 找到返回对应的下标,没找到则返回-1
     */
    private static int binarySearch(int[] arr, int left, int right, int findVal) 
        //当 left > right 时,说明递归整个数组,但是没有找到,此时直接返回-1
        if (left > right) 
            return -1;
        

        int mid = (left + right) / 2;
        int midVal = arr[mid];
        if (findVal > midVal) 
            return binarySearch(arr, mid + 1, right , findVal);
         else if (findVal < midVal) 
            return binarySearch(arr, left, mid - 1, findVal);
         else 
            return mid;
        
    

    /**
     * 有多个相同的数值时,如何将所有的数值都查找到,比如这里的 1000
     * 思路分析
     * 	 1. 在找到mid 索引值,不要马上返回
     * 	 2. 向mid 索引值的左边扫描,将所有满足 1000 的元素的下标,加入到集合ArrayList
     * 	 3. 向mid 索引值的右边扫描,将所有满足 1000 的元素的下标,加入到集合ArrayList
     * 	 4. 将Arraylist返回
     */
    private static List<Integer> binarySearch2(int[] arr, int left, int right, int findVal) 
        //当 left > right 时,说明递归整个数组,但是没有找到,此时直接返回空list
        if (left > right) 
            return new ArrayList<>();
        

        int mid = (left + right) / 2;
        int midVal = arr[mid];
        if (findVal > midVal) 
            return binarySearch2(arr, mid + 1, right , findVal);
         else if (findVal < midVal) 
            return binarySearch2(arr, left, mid - 1, findVal);
         else 
            List<Integer> resIndexList = new ArrayList<>();
            //向 mid 索引值的左边扫描,将所有满足 1000 的元素的下标,加入到集合ArrayList
            int temp = mid - 1;
            while (true) 
                //索引小于0表示已到达数组最左边,arr[temp] != findVal表示找到不等于findVal的就可以退出了
                if (temp < 0 || arr[temp] != findVal) 
                    break;
                
                resIndexList.add(temp); //找到了就加入list中
                temp--; //因为是向左查找,所以索引值依次-1
            
            //别忘了,还要将最先查找到的mid下标加入list中
            resIndexList.add(mid);
            //向mid 索引值的右边扫描,将所有满足 1000 的元素的下标,加入到集合ArrayList
            temp = mid + 1;
            while (true) 
                //索引大于arr.length - 1表示已到达数组最右边,arr[temp] != findVal表示找到不等于findVal的就可以退出了
                if (temp > arr.length - 1 || arr[temp] != findVal) 
                    break;
                
                resIndexList.add(temp); //找到了就加入list中
                temp++; //因为是向右查找,所以索引值依次+1
            
            return resIndexList;
        
    

    public static void main(String[] args) 
        int[] arr =  1, 2, 3, 4, 5, 6, 7, 8, 9, 10 , 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ;
		int resIndex = binarySearch(arr, 0, arr.length - 1, 14);
		System.out.println("resIndex = " + resIndex);

        System.out.println("---------------------------------");

        int[] array =  1, 8, 10, 89, 1000, 1000, 1000, 3333, 9527;
        List<Integer> list = binarySearch2(array, 0, array.length - 1, 1000);
        System.out.println(list);
    

运用二分查找算法,在n个元素的数组中查找一个数,情况最遭时,需要(log2 n)步,所以二分查找的时间复杂度是O(log2 n)


3.插值查找

插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid处开始查找。

将折半查找中的求mid 索引的公式 , low 表示左边索引left, high表示右边索引right. key 就是上面代码中的  findVal。

    改成→ 

int mid = low + (high - low) * (key - arr[low]) / (arr[high] - arr[low])  ;/*插值索引*/

对应前面的代码公式: int mid = left + (right – left) * (findVal – arr[left]) / (arr[right] – arr[left]) 

package com.szh.search;

/**
 * 插值查找
 */
public class InsertValueSearch 

    private static int insertValueSearch(int[] arr, int left, int right, int findVal) 
        int num = 0;
        System.out.println("插值查找的次数:" + (++num));

        //注意:findVal < arr[0]  和  findVal > arr[arr.length - 1] 必须需要
        //否则我们得到的 mid 可能越界
        if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) 
            return -1;
        
        //求出mid, 自适应 (插值查找的核心就是下面这行代码)
        int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
        int midVal = arr[mid];
        if (findVal > midVal)  //说明应该向右边递归
            return insertValueSearch(arr, mid + 1, right, findVal);
         else if (findVal < midVal)  //说明向左递归查找
            return insertValueSearch(arr, left, mid - 1, findVal);
         else 
            return mid;
        
    

    public static void main(String[] args) 
        int[] arr =  1, 2, 3, 4, 5, 6, 7, 8, 9, 10 , 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ;
        int resIndex = insertValueSearch(arr, 0, arr.length - 1, 14);
        System.out.println("查找元素的下标 resIndex = " + resIndex);
    

插值查找注意事项:

  1. 对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找, 速度较快。
  2. 关键字分布不均匀的情况下,该方法不一定比折半查找要好。

以上是关于Java数据结构与算法——线性查找 & 二分查找 & 插值查找的主要内容,如果未能解决你的问题,请参考以下文章

学习数据结构笔记 ---查找算法(线性查找,二分查找,插值查找,斐波那契查找)

[算法与数据结构] 谈谈线性查找法~

[算法与数据结构] 谈谈线性查找法~

[算法与数据结构] 谈谈线性查找法~

[算法与数据结构] 谈谈线性查找法~

数据结构与算法(Java)之查找