• 三种常用的查找算法


    在最近的复习中,我复习三种常用的查找算法,它们分别是:

    1 线性查找

    2 二分查找

    3 插值法

    4 斐波那契查找

    线性查找

    首先我们进行线性查找的讨论,对于线性查找,我们所做的操作就遍历数组同时逐一比对找出相匹配的元素,具体代码如下

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

    又上述代码我们不难理解,线性查找就是遍历数组的同时逐一比对数组的值和要查找的值,如果相同则就返回对应的数组下标

    二分查找

    对于二分查找,我们首先计算mid的值,在此演示中,我们设置的mid值为(left+right)/2,如果要搜索的值大于数组下标为mid的值,则就将left的值设置为mid+1,继续遍历,如果要插入的值小于数组下标为mid的值,则就将right的值设置为mid-1,继续遍历,重复上述操作,直到找到对应的数组的下标为止,当发生left>right时,则说明数组中并没有要搜索的值,此时我们返回-1并结束循环。

      public static int binarySearch(int[] arr,int left,int right,int findVal)
        {
            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;
            }
        }
      //第二种方式是找出所有对应的数据才停止
    public static List<Integer> binarySearch2(int[] arr,int left,int right,int findVal) { if(left>right) { return new ArrayList<Integer>(); } 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> list=new ArrayList<Integer>(); int temp=mid-1; while (true) { if(temp<0||arr[temp]!=findVal) { break; } //否则就把temp放入到集合中 list.add(temp); temp-=1; } list.add(mid); temp=mid+1; while (true) { if(temp>arr.length-1||arr[temp]!=findVal) { break; } //否则就把temp放入到集合中 list.add(temp); temp+=1; } return list; } }

    在经过分析时,我们发现搜索边缘的操作对于二分查找太过繁琐,所以我们引入插值法来进行搜索

    插值法

    插值法与二分查找相同,不过mid值有所改变

      /**
         * 插值法和二分查找法一样,查找数组必须是有序的
         * @param arr
         * @param left
         * @param right
         * @param findVal
         * @return
         */
        public static int insertValueSearch(int[] arr,int left,int right,int findVal)
        {
            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;
            }
        }

    与二分查找方法不同是,插值法的mid等于low+(high-left)*(findVal-arr[low])/(arr[high]-arr[low]),我们在此基础上继续查找边缘位,如有一个大小为100的数组,第一位是0,最后一位是100,我们使用该方法查找0 100时会发现,我们第一次求出的mid的值就是对应的下标,所以插值法简化了二分查找。

    斐波那契查找

    斐波那契查找与二分查找很相似,他是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,即n=F(k)-1;(n为数组的大小,如果数组大小小于F(k)-1,则我们会将数组填充,即拿数组最后一位填充,直到n大于等于F(k)-1时)

     开始将k值与第F(k-1)位置的记录进行比较(及mid=low+F(k-1)-1),比较结果也分为三种

     1)相等,mid位置的元素即为所求

     2)key>arr[mid],low=mid+1,k-=2;说明:low=mid+1说明待查找的元素在[mid+1,hign]范围内,k-=2 说明范围[mid+1,high]内的元素个数为n-(F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1个,所以可以递归的应用斐波那契查找

     3)  key<,arr[mid],high=mid-1,k-=1;说明:low=mid+1说明待查找的元素在[low,mid-1]范围内,k-=1 说明范围[low,mid-1]内的元素个数为F(k-1)-1个,所以可以递归的应用斐波那契查找

        private static int maxSize=20;
        public static void main(String[] args)
        {
            int[] arr={1,8,10,89,1000,1234};
        }
        //因为后面我们的mid=low+F(k-1)-1,需要使用斐波那契数列,因此我们需要先获取到一个斐波那契数列
        //非递归方法获取斐波那契数列
        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;
        }
        //编写斐波那契查找算法
        //使用非递归的方式编写算法
        /**
         *
         * @param a 数组
         * @param key 我们需要的关键码(值)
         * @return 返回对应的下标,如果没有返回-1
         */
        public static int fibSearch(int[] a,int key)
        {
            int low=0;
            int high=a.length-1;
            int k=0;//表示斐波那契分割数值的下标
            int mid=0;//存放mid的值
            int f[]=fib();//获取到斐波那契数列
            while (high>f[k]-1)
            {
                k++;
            }
            //因为f[k]的值可能大于数组长度,因此我们需要使用Arrays类,构造一个新的数组,并指向temp
            //不足的部分用0填充
            int[] temp= Arrays.copyOf(a,f[k]);
            //实际上需求使用a数组最后的数填充temp
            for(int i=high+1;i<temp.length;i++)
            {
                temp[i]=a[high];
            }
            //使用while来循环处理,找到我们的数key
            while (low<=high)
            {
                //只要这个条件满足就可以找
                mid=low+f[k-1]-1;
                if(key<temp[mid])
                {
                    //我们应该继续向数组的左边查找
                    high=mid-1;
                    //为什么是k--
                    //说明:
                    //1 全部的元素=前面的元素+后面的元素
                    //2 f[k]=f[k-1]+f[k-2]
                    //因为前面有f[k-1]个元素,所以我们可以继续拆分
                    //即在f[k-1]的前面继续查找,即下次循环mid=f[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-2]的前面可以继续进行查找k-=2
                    //即下次循环mid=f[k-1-2]-1
                    k-=2;
                }else{
                    //需要确定返回的是哪个下标
                    if(mid<high)
                    {
                        return mid;
                    }else {
                        return high;
                    }
                }
            }
            return -1;
        }
  • 相关阅读:
    ZOJ 1450
    HDU 3932
    POJ 3348
    POJ 1873
    POJ 1228
    POJ 2007
    POJ 1113
    POJ 1696
    POJ 1329
    HDU 3432
  • 原文地址:https://www.cnblogs.com/qyx66/p/11959968.html
Copyright © 2020-2023  润新知