数据结构小白之基础查找算法(线性查找,二分查找,基数查找)

1.线性查找的思路

线性查找是一种最简单粗暴的查找法了,采用逐一比对的方式进行对数组的遍历,如果发现了匹配值,返回数组下标即可

package com.liz.seq_search;

public class SeqSearchDemo {
    private static int count=0;
    public static void main(String[] args) {
        //无序序列
        int arr[] = {1, 9, 11, -1, 34, 55};
        int index=seqSearch(arr,55);
        if(index==-1){
            System.out.println("未找到");
        }
        System.out.println("代码执行了"+count+"次"+" "+"找到的位置为"+index);

    }

    //线性查找: 逐一比对,发现有相同的值时,返回下标
    public static int seqSearch(int[] arr, int value) {

        for (int i = 0; i < arr.length; i++) {
            count++;
            if(arr[i]==value){
                return i;
            }
        }
        return -1;
    }
}

2.二分查找法的思路

2-1: 首先要确定该数组中间的 下标

mid=(left+right)/2

2-2:然后让需要查找的数findVal和arr[mid]做比较

    findVal>arr[mid],说明了你要查找的数在mid的右边,因此需要递归向右查找
    findVal<arr[mid],说明了你要查找的数在mid的左边,因此需要递归向左查找
    findVal==arr[mid],说明找到了,需要返回

2-3:确定递归结束的条件

*找到目标数就结束递归

*递归完整个数组依旧没有找到目标数,也要结束递归

2-4: 举个栗子

public static int binarySearch(int[] arr, int left, int right, int findVal) {
        count++;
        int mid = (left + right) / 2;
        int midVal = arr[mid];

        //如果没有找到
        if (left > right) {
            return -1;
        }
        //向右递归
        if (findVal > midVal) {
            return binarySearch(arr, mid + 1, right, findVal);
        } else if (findVal < midVal) {//向左递归
            return binarySearch(arr, left, mid - 1, findVal);
        } else {
            return mid;
        }
    }

public static void main(String[] args) {
        int arr[] = {1, 8, 10, 89, 1000,1000, 1234};
        int resultIndex = binarySearch(arr, 0, arr.length, 10);
        if (resultIndex == -1) {
            System.out.println("未找到当前数据");
            return;
        }
        System.out.println(resultIndex);
//        ArrayList<Integer>list=binarySearchUp(arr,0,arr.length,12);
//        if(list.size()==0){
//            System.out.println("未找到当前数据");
//            return;
//        }
//        System.out.println(list);
    }

注:使用这种方式进行数据的寻找虽然可行,但是如果在数组存在重复的目标值,只能定位一个下标,无法定位所有的值。

(在这里反应的是,虽然有两个1000 位置分别是4 和 5 ,但是只定位了一个5)

所以,我们可以采用一些方法进行优化,将原来方法中返回一个具体的mid,改成返回一个带有mid标志位的容器即可

(本栗子中采用了ArrayList进行存储中间值)

public static ArrayList binarySearchUp(int[] arr, int left, int right, int findVal) {
        int mid = (left + right) / 2;
        int midVal = arr[mid];

        //如果没有找到
        if (left > right) {
            return new ArrayList<Integer>();
        }
        //向右递归
        if (findVal > midVal) {
            return binarySearchUp(arr, mid + 1, right, findVal);
        } else if (findVal < midVal) {//向左递归
            return binarySearchUp(arr, left, mid - 1, findVal);
        } else {
            //这里已经找到了mid值
            ArrayList<Integer> resIndexList=new ArrayList<Integer>();

            //向左扫描
            int temp=mid-1;
            while(true){
                //已经无法再向左扫描了
                if(temp<0 || arr[temp]!=findVal){
                    break;
                }
                //否则就把temp放入到集合中
                resIndexList.add(temp);
                temp--;
            }
            resIndexList.add(mid);

            //向右扫描
            temp=mid+1;
            while(true){
                //已经无法再向右扫描了
                if(temp>arr.length-1 || arr[temp]!=findVal){
                    break;
                }
                //否则就把temp放入到集合中
                resIndexList.add(temp);
                temp++;
            }
            return resIndexList;
        }

    }
public static void main(String[] args) {
        int arr[] = {1, 8, 10, 89, 1000,1000, 1234};
//        int resultIndex = binarySearch(arr, 0, arr.length, 1000);
//        if (resultIndex == -1) {
//            System.out.println("未找到当前数据");
//            return;
//        }
//        System.out.println("代码执行了"+count+"次"+" "+"找到的位置为:"+resultIndex);
        ArrayList<Integer>list=binarySearchUp(arr,0,arr.length,12);
        if(list.size()==0){
            System.out.println("未找到当前数据");
            return;
        }
        System.out.println("找到的索引值为"+list);
    }

3.基数查找的思路

基数查找和二分查找执行原理上差不多,但是和二分查找的区别在于对于mid的定位

基数查找定位mid:

mid= (left+right)/2

二分查找定位mid: 

mid= left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);

举个栗子:

package com.liz.insertval_search;

//采取自适应mid的方法进行
//自适应公式: left+(right-low)*(findVal-arr[left])/(arr[high]-arr[low])

/**
 * 注意事项:
 *      对于数据量较大,关键词分布比较均匀的查找而言,采用插值查找速度较快
 *      关键字分布不均匀的情况下,该方法不一定比折半查找法好
 * */
public class InsertValSearchDemo {
    public static int count=0;
    public static void main(String[] args) {
        int arr[] = new int[100];
        for (int i = 0; i < 100; i++) {
            arr[i] = i + 1;
        }
        int midValue=insertValSearch(arr,0,arr.length-1,20);
        System.out.println("代码执行了"+count+"次"+" "+"找到的位置为"+midValue);

    }

    public static int insertValSearch(int[] arr, int left, int right, int findVal) {
        count++;
        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 insertValSearch(arr, mid + 1, right, findVal);
        } else if (findVal < midVal) {//说明应该向左进行递归
            return insertValSearch(arr,left,mid-1,findVal);
        }else{
            return mid;
        }
    }
}

(在本栗子中对100个连续数字采用插值查找,通过自适应mid,仅仅用了一次就将结果查询出来了)

如果将相同的测试用例放在二分法中,大家不妨试试

注:插值查找法可以说是二分查找法在既定条件下的一种优化,对于数据量较大,数据分布比较均匀的查找而言,采用插值查找速度较快。但是如果数据分布不均,二分查找法不一定比插值查找法差。

发布了193 篇原创文章 · 获赞 70 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/Lzinner/article/details/103029591