• 0085 查找算法


     

     

     

     

     

     

     

     

     

    /*
     * 常用查找算法:
     * 1.顺序(线性)查找
     * 2.二分查找(折半查找)
     * 3.插值查找
     * 4.斐波那契查找
     */
    /*
     * 顺序(线性)查找
     * 逐一比对,发现相同返回下标
     */
    public class SequenceSearch_ {
        public static void main(String[] args) {
            int[] arr = {1,9,11,-1,34,89};
            int index = seqSearch(arr, 11);
            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;
        }
    }

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

    /*
     * 二分查找
     * 前提:数组必须有序
     * 1.确定该数组的中间值下标 mid=(left+right)/2
     * 2.让需要查找的数findVal和arr[mid]比较
     *         1.如果findVal>arr[mid],递归向右查找
     *         2.如果findVal  *         3.如果findVal=arr[mid],直接返回
     * 
     * 结束递归:
     * 1.找到结束递归
     * 2.递归完整个数组,仍找不到,也结束递归 left>right            
     *         
     */
    public class BinarySearch_ {
        public static void main(String[] args) {
            int[] arr = {1,8,10,89,1000,1234};
            int resIndex = binarySearch(arr, 0, arr.length, 1);
            System.out.println("索引=" + resIndex);
            
            int[] arr2 = {1,8,10,10,10,10,10,10,10,89,1000,1234};
            List res = binarySearch2(arr2, 0, arr.length - 1, 10);
            System.out.println("索引=" + res);
        }
        
        //二分查找
        //arr:数组,left:左边索引,right:右边索引,findVal:查找值,如果找到返回下标,没有返回-1
        public static int binarySearch(int[] arr,int left,int right,int findVal) {
            //当left > right时,说明没有找到
            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;
            }
        }
        
        //如果有多个相同的数值,如何返回全部下标?
        //1.找到mid索引值时,不要马上返回
        //2.向mid索引值的左边扫描,将所有满足元素的下标,加入到集合ArrayList
        //3.向mid索引值的右边扫描,将所有满足元素的下标,加入到集合ArrayList
        //4.将ArrayList返回
        public static List binarySearch2(int[] arr,int left,int right,int findVal) {
            //当left > right时,说明没有找到
            if (left > right) {
                return new ArrayList();//返回空的List
            }
            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 {
                //向mid索引值的左边扫描
                List arrayList = new ArrayList();
                int temp = mid - 1;
                while(true) {
                    if (temp < 0 || arr[temp] != findVal) {
                        break;
                    }
                    arrayList.add(temp);//放入到集合中
                    temp -= 1;//temp左移
                }
                arrayList.add(mid);
                
                //向mid索引值的右边扫描
                temp = mid + 1;
                while(true) {
                    if (temp > arr.length - 1 || arr[temp] != findVal) {
                        break;
                    }
                    arrayList.add(temp);//放入到集合中
                    temp += 1;//temp右移
                }
                return arrayList;
            }
        }
    }

    /*
     * 插值查找
     * 前提:数组必须有序
     * 1.类似于二分查找,不同的是插值查找每次从自适应mid处开始查找
     * 2.low表示左索引left,high表示右索引right,key表示查找值findVal
     *   二分查找中间值下标:mid=(low+high)/2=low+1/2(high-low)
     *      插值查找中间值下标:mid=low+(key-arr[low])/(arr[high]-arr[low])(high-low)
     *   对应前面代码,即
     *      int mid = left + (right-left) * (findVal - arr[left]) / (arr[right] - arr[left])
     * 
     * 插值查找的优越性
     * 数组arr={1,2,3,4......99,100};
     * 查找1
     * 使用二分查找需要多次递归
     * 使用插值查找:mid = 0+(99-0)*(1-1)/(100-1)=0 一步定位
     * 
     * 使用
     * 1.对于数据量较大,关键字分布较均匀的查找表来说,采用插值查找速度较快
     * 2.关键字分布不均匀的情况下,不一定比二分查找好
     */
    public class InsertValueSearch_ {
        public static void main(String[] args) {
            //数组arr={1,2,3,4......99,100};
            int[] arr = new int[100];
            for(int i = 0;i < 100;i++) {
                arr[i] = i + 1;
            }
            int index = insertValueSearch(arr, 0, arr.length - 1,1);
            System.out.println("index=" + index);
            int index2 = binarySearch(arr, 0, arr.length, 1);
            System.out.println("index=" + index2);
        }
        
        //插值查找
        //arr:数组,left:左边索引,right:右边索引,findVal:查找值,如果找到返回下标,没有返回-1
        public static int insertValueSearch(int[] arr,int left,int right,int findVal) {
            System.out.println("插值查找被调用");
            //findVal < arr[0] || findVal > arr[arr.length - 1]必须需要,否则可能越界
            if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {
                return -1;
            }
            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;
            }
        }
        
        //二分查找
        //arr:数组,left:左边索引,right:右边索引,findVal:查找值,如果找到返回下标,没有返回-1
        public static int binarySearch(int[] arr,int left,int right,int findVal) {
            System.out.println("二分查找被调用");
            //当left > right时,说明没有找到
            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;
            }
        }
    }

    import java.util.Arrays;

    /*
     * 斐波那契查找(黄金分割法)
     * 前提:数组必须有序
     * 1.黄金分割点是指把一条线段分割为两部分,使其中一部分与全长之比=另一部分与这部分之比,取其前三位数字的近似值为0.618
     *   由于按此比例的造型十分美丽,称为黄金分割,也称中外比
     * 2.斐波那契数列{1,1,2,3,5,8,13,21,34,55}的两个相邻数的比例,无限接近黄金分割值0.618
     * 
     * 斐波那契查找和前两种相似,只是改变了中间结点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分割点附近
     * 即mid=low+F(k-1)-1(F代表斐波那契数列)
     * 
     * 对F(k-1)-1的理解
     * 1.由斐波那契数列F[k]=f[k-1]+F[k-2]的性质可得,(F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+1
     *      即只要顺序表的长度为F[k]-1,则可以将该表分成长度为F[k-1]-1和F[k-2]-1的两段
     *   从而中间位置mid=low+F(k-1)-1
     * 2.类似的,每一字段也可以用相同的方式分割
     * 3.顺序表长度n不一定刚好等于F[k]-1,需要将原来的顺序表长度n增加至F[k]-1,这里的k值只要能使得F[k]-1恰好大于或等于n即可
     *   while(n>fib(k)-1)
     *         k++
     *      由以上代码得,顺序表长度增加后,新增的位置都赋为n位置的值即可
     */
    public class FibonacciSearch_ {
        public static int maxSize = 20;
        public static void main(String[] args) {
            int[] arr = {1,8,10,89,1000,1234};
            System.out.println("index=" + fibSearch(arr,1000));
        }
        
        //用非递归的方式获取斐波那契数列
        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;
        }
        
        //斐波那契查找
        //arr:数组,key:查找值,如果找到返回下标,没有返回-1
        public static int fibSearch(int[] arr,int key) {
            int low = 0;
            int high = arr.length - 1;
            int k = 0;//表示斐波那契分割数值的下标
            int mid = 0;//存放mid值
            int f[] = fib();//获取斐波那契数列
            //获取斐波那契分割数值下标k
            while(high > f[k] - 1) {
                k++;
            }
            //因为f[k]的值可能大于它的arr长度,因此需要使用Array类,构造一个新的数组,并指向temp[],不足的部分会使用0填充
            int[] temp = Arrays.copyOf(arr, f[k]);
            //用arr数组最后的数填充temp
            for(int i = high + 1;i < temp.length;i++) {
                temp[i] = arr[high];
            }
            //使用while循环处理,找到key
            while(low <= high) {//只要这个条件满足,就可以找
                mid = low + f[k-1]-1;
                if (key < temp[mid]) {//继续向数组前面查找(左边)
                    high = mid - 1;
                    //1.全部元素 = 前面元素+后面元素
                    //2.f[k] = f[k-1]+f[k-2]
                    //3.因为前面有f[k-1]个元素,所以可以继续拆分f[k-1] = f[k-2]+f[k-3]
                    //即在f[k-1]的前面继续查找,k--,即下次循环mid=k[k-1-1]-1
                    k--;
                }else if (key > temp[mid]) {//继续向数组后面查找(右边)
                    low = mid + 1;
                    //1.全部元素 = 前面元素+后面元素
                    //2.f[k] = f[k-1]+f[k-2]
                    //3.因为后面有f[k-2]个元素,所以可以继续拆分f[k-1] = f[k-3]+f[k-4]
                    //即在f[k-2]的前面进行查找,k-=2,即下次循环mid=f[k-1-2]-1
                    k -= 2;
                }else {//找到了
                    if (mid <= high) {
                        return mid;
                    }else {
                        return high;
                    }
                }
            }
            return -1;
        }
    }

     

     

  • 相关阅读:
    软考是什么?---2023年软考最全解析
    ROS_关键索引=3=
    云表格Pro功能上新:透视表视图+一键出图功能来啦!
    23种设计模式之单例模式
    通达OA_文件包含漏洞
    MySQL日志管理、备份与恢复
    Java 集合 - Map 接口
    leetcode 42. 接雨水-java
    安卓导航抽屉 Navigation Drawer 实现沉浸通知栏
    [MAUI]模仿Chrome下拉标签页的交互实现
  • 原文地址:https://blog.csdn.net/m0_72797089/article/details/127708241