数据结构-四种查找算法

一、个人感悟

1)线性查找

缺点非常明显,耗费的时间太长
优点是可以查找没有排好序的数组

2)二分查找

mid的算法是 left+(right - left)/2
缺点是需要排好序的数组
优点是对于线性查找大大减少耗费时间

3)插值查找

原理与二分查找类似,但是mid拥有了适应性
mid的算法变为 left+(right - left) * (findVal - arr[left]) / (arr[right] - arr[left])
这种算法非常适用于平均分布的数组比如{1,2,3,4—— 97,98,99,100}
缺点是需要排好序,如果分布跨度很大的话速度可能还不如二分查找
优点是如果分布均匀可以很快的找到目标数字

4)斐波那契查找

斐波那契数列:{1,1,2,3,5,8,13,21…}
与二分法不同的地方在于,运用了黄金分割比,
先建立一个斐波那契数列,然后mid每次的取值都是符合原数列的黄金分割比的,
在把原数列长度补到k指向的斐波那契数列里的值后,mid = left + f(k-1) -1,这样也就把数组分割为了左长右短两部分,如果比mid指向的值大的话,需要用到小的那一块数组,就把斐波那契数组中k往左移两个,如果要用到大的那一块,就左移一次

总结:

其实二分、插值、斐波那契的基本原理都是建立在二分之上的,差别只是mid的取值。二分比较万金油,插值更适合分布均匀,斐波那契我网上找了找是这样说的

与二分查找相比,斐波那契查找算法的明显优点在于它只涉及加法和减法运算,而不用除法。因此,斐波那契查找的平均性能要比折半查找好。

可能就像毕导套娃那期视频里说的,无论对计算机还是人来说加法永远比乘法好算吧

二、代码

1)线性查找

package 查找.线性查找;

public class SeqSearch {
    public static void main(String[] args) {
        int arr[] = {1,9,11,-1,34,89};
        int index = seqSearch(arr,-1);
        if (index == -1){
            System.out.println("没找到");
        }else {
            System.out.println("下标为"+index);
        }

    }

    public static int seqSearch(int[] arr, int value){

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

2)二分查找

package 查找.二分查找;

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

public class BinarySearch {
    public static void main(String[] args) {
//        int[] arr = {1, 8, 10, 89, 1000, 1000, 1000, 1234};
        int[] arr= {1,3,8,15,19,25,36,38,48,56,68,79,88,99,105,112,118,200};

//        List<Integer> list = binarySearch2(arr, 0, arr.length - 1, 5);
        int i = binarySearch(arr, 0, arr.length - 1, 1);
        System.out.println(i);
//        System.out.println(list);

    }

    public static int binarySearch(int[] arr, int left, int right, int findVal) {
        System.out.println("被调用");
        if (left > right) {
            return -1;
        }
        int mid = (left + right) / 2;

        if (arr[mid] < findVal) {
            return binarySearch(arr, mid + 1, right, findVal);
        } else if (arr[mid] > findVal) {
            return binarySearch(arr, left, mid - 1, findVal);
        } else {
            return mid;
        }

    }

    //找出每一个数字的下标
    public static List<Integer> binarySearch2(int[] arr, int left, int right, int findVal) {
        if (left > right) {
            return null;
        }
        int mid = (left + right) / 2;

        if (arr[mid] < findVal) {
            return binarySearch2(arr, mid + 1, right, findVal);
        } else if (arr[mid] > findVal) {
            return binarySearch2(arr, left, mid - 1, findVal);
        } else {
            List<Integer> resIndexList = new ArrayList<>();

            int temp = mid - 1;
            while (temp >= 0 && arr[temp] == findVal) {
                resIndexList.add(temp);
                temp--;
            }
            resIndexList.add(mid);
            temp = mid + 1;
            while (temp <= arr.length - 1 && arr[temp] == findVal) {
                resIndexList.add(temp);
                temp++;
            }
            return resIndexList;
        }

    }
}

3)插值查找

package 查找.插值查找;

public class InsertValueSearch {
    public static void main(String[] args) {
//        int[] arr = new int[100];
//        for (int i = 0; i < 100; i++) {
//            arr[i] = i+1;
//        }
        int[] arr= {1,3,8,15,19,25,36,38,48,56,68,79,88,99,105,112,118,200};

        int i = insertValueSearch(arr,0,arr.length-1,1);
        System.out.println(i);
//        System.out.println(Arrays.toString(arr));
    }

    public static int insertValueSearch(int[] arr,int left, int right, int findVal){

        System.out.println("被调用");

        if (left > right || findVal < arr[0] || findVal > arr[arr.length-1]){
            //为什么要比二分法多了两个比较?
            //因为如果findVal如果非常大或者非常小在下面的程序中会导致mid很大或很小,就有可能就越界
            return -1;
        }
        int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
        if (arr[mid] < findVal) {
            return insertValueSearch(arr, mid + 1, right, findVal);
        } else if (arr[mid] > findVal) {
            return insertValueSearch(arr, left, mid - 1, findVal);
        } else {
            return mid;
        }
    }
}

4)斐波那契查找

package 查找.斐波那契;

import java.util.Arrays;

public class FibonacciSearch {

    public static int maxSize = 20;

    public static void main(String[] args) {
        int[] arr= {1,3,8,15,19,25,36,38,48,56,68,79,88,99,105,112,118,200};


        System.out.println(fibSearch(arr,3));

    }

    //非递归的获得一个斐波那契数列
    public static int[] fib(){
        int[] f = new int[maxSize];
        f[0] = 1;
        f[1] = 1;
        for (int i = 2; i < maxSize; i++) {
            f[i] = f[i-1] + f[i-2];
        }
        return f;
    }

    //编写斐波那契查找算法
    public static int fibSearch(int[] arr, int key){
        int left = 0;
        int right = arr.length-1;
        int k = 0;//表示斐波那契数组的下标
        int mid = 0;
        int f[] = fib();

        //获得斐波那契数列数值的下标
        while (f[k] < right + 1){
            k++;
        }

        //得到的k指向的数值可能比数值长度大,把数组长度补上
        int[] temp = Arrays.copyOf(arr,f[k]);
        //多出来的地方补上数组的最后一位数
        for (int i = right +1; i < temp.length; i++) {
            temp[i] = arr[right];
        }

        while (left <= right){
            System.out.println("被调用");
            mid = left + f[k-1] -1;//后一个减一是因为要的是数组下标,而f里的是长度
            if (arr[mid] > key){
                right = mid -1 ;
                k--;//长度变成了分割成的两块里头较大的那一块
            }else if (arr[mid] < key){
                left = mid+1;
                k-=2;//长度变为了分割成的两块里头教小的那一块
            }else if (arr[mid] == key){
                //有可能找到的是前面生成的那些数
                if (mid > left){
                    return left;
                }else {
                    return mid;
                }
            }

        }
        return -1;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值