本篇文章:

主要是关于java数据结构与算法的一些基本知识:查找算法。

正文如下:

一、查找算法

1)查找算法的介绍:

java中,我们常用的查找算法有四种:

① 顺序(线性)查找

② 二分查找/折半查找

③ 插值查找

④ 斐波那契查找

2)线性查找算法:

① 线性查找算法实例:

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

package com.immort.dataStructure.search;

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

/**
 * 线性查找算法:在数组中线性查找指定数据
 * @author ppmy
 * @create 2021-03-31 09:05
 */
public class Study0SeqSearchDemo1 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,1,3,4,1,1,3};
        //查找数据3
        List<Integer> list = seqSearch(array, 3);
        //获取迭代器
        Iterator<Integer> it = list.iterator();

        //遍历迭代器
        while (it.hasNext()){
            System.out.print(it.next()+" ");
        }
    }

    /**
     * 在数组中查找某一个数据
     * @param array 要查找的数组
     * @param values 要查找的数据
     * @return 返回要查找数据的对应下标的集合
     */
    private static List<Integer> seqSearch(int[] array, int values){
        List<Integer> arrays = new ArrayList<>();

        for (int i = 0; i < array.length; i++){
            if(array[i] == values){
                arrays.add(i);
            }
        }
        return arrays;
    }
}

3)二分查找算法

① 二分查找算法的介绍

当我们要从一个序列中查找一个元素的时候,二分查找是一种非常快速的查找算法,二分查找又叫折半查找。它对要查找的序列有两个要求,一是该序列必须是有序的(即该序列中的所有元素都是按照大小关系排好序的,升序和降序都可以。

② 二分查找算法的原理【该数组为升序数组】

1)如果左索引大于右索引的时候,无法找到该数据,返回-1,并退出算法。

2)如果待查序列不为空,则将它的中间元素与要查找的目标元素进行匹配,看它们是否相等。

3)如果相等,则返回该中间元素的索引,并退出算法;此时就查找成功了。

4)如果不相等,就再比较这两个元素的大小。

5)如果该中间元素大于目标元素,那么就将当前序列的前半部分作为新的待查序列;这是因为后半部分的所有元素都大于目标元素,它们全都被排除了。

6)如果该中间元素小于目标元素,那么就将当前序列的后半部分作为新的待查序列;这是因为前半部分的所有元素都小于目标元素,它们全都被排除了。

7)在新的待查序列上重新开始第1步的工作。

二分查找之所以快速,是因为它在匹配不成功的时候,每次都能排除剩余元素中一半的元素。因此可能包含目标元素的有效范围就收缩得很快,而不像顺序查找那样,每次仅能排除一个元素。

③ 二分查找算法的分析

java查找内容并选中 java中的查找算法_数据

④ 二分查找算法代码:

package com.immort.dataStructure.search;

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

/**
 * 二分查找:有序数组才可以进行二分查找
 * @author ppmy
 * @create 2021-03-31 09:29
 */
public class Study1BinarySearchDemo1 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6,6,6,6,6,6,7,8,9,10};
        List<Integer> list = new ArrayList<>();
//        int i = binarySearch(array, 0, array.length-1, 11);
        binarySearch1(list,array, 0, array.length-1, 6);



//        if(i==-1){
//            System.out.println("没有找到这个数 ");
//        }else {
//            System.out.println("数据位置在"+i);
//        }

        list.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        for (int i:list){
            System.out.print(i+" ");
        }



    }

    /**
     * 升序排序下的二分查找算法:返回当前数组中第一个匹配数据的索引值
     * @param array 要进行查找的数组
     * @param left 左索引
     * @param right 右索引
     * @param value 要查找的数据
     * @return 查找数据的索引:未找到: -1:找到:索引值
     */
    //二分查找算法
    private static int binarySearch(int[] array, int left, int right,int value){
        //递归结束的条件1:
        //如果当前的left大于了right,表示没有找到该数据
        if(left > right){
            return -1;
        }

        //获取中间值
        int middle = (left+right)/2;

        //要查找到的值,在当前middle的右边
        if(array[middle]<value){
            return binarySearch(array, middle+1, right, value);
        }
        //要查找的值在middle的左边
        else if(array[middle]>value){
            return binarySearch(array, left, middle-1, value);
        }
        //递归结束的条件2:
        //查找到返回对应下标
        else{
            return middle;
        }
    }

    /**
     * 	返回当前数组中所有匹配数据的索引值集合
     * @param list 封装查找到的数据索引的集合
     * @param array 要进行查找的数组
     * @param left 左索引
     * @param right 右索引
     * @param value 要查找的数据
     */
    //二分查找算法
    private static void binarySearch1(List<Integer> list,int[] array, int left, int right,int value){
        //递归结束的条件:
        //如果当前的left大于了right,表示没有找到该数据
        if(left > right){
            return;
        }

        //获取中间值
        int middle = (left+right)/2;

        //要查找到的值,在当前middle的右边
        if(array[middle]<value){
            binarySearch1(list,array, middle+1, right, value);
        }
        //要查找的值在middle的左边
        else if(array[middle]>value){
            binarySearch1(list,array, left, middle-1, value);
        }

        //查找到了指定的元素
        //1.将该数据放到
        else{

            //将找到的数据的索引添加到集合中
            list.add(middle);

            int index = middle -1;
            while (index >= 0){
                if(array[index]==value){
                    list.add(index);
                    index--;
                }else{
                    break;
                }
            }
            //向右进行扫描,扫描是否存在其他相等的值
            index = middle+1;
            while (index<array.length){
                if(array[index]==value){
                    list.add(index);
                    index++;
                }else{
                    break;
                }
            }
            return;
        }
    }
}

4)插值查找算法【查找较为方便】:

① 插值查找算法的介绍:

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

2)将折半查找中的求mid 索引的公式 ,更改为:

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

② 插值查找算法的图解分析:

java查找内容并选中 java中的查找算法_数据结构_02

③ 插值查找的代码:

package com.immort.dataStructure.search;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/**
 * 插值查找算法:适合数据分布较为均匀的时候,查找数据的次数会大大的减小
 * @author ppmy
 * @create 2021-04-02 10:49
 */
public class Study2InterSearchDemo1 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6,6,6,6,7,7,7,8,8,8};
        List<Integer> list = new ArrayList<>();
        insertSort(list, array, 0, array.length-1, 6);
        list.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        Iterator<Integer> it = list.iterator();

        while (it.hasNext()){
            System.out.print(it.next()+" ");
        }

    }

    /**
     * 使用插值算法查找数组中是否存在某数据
     * @param list 用来存储索引的集合
     * @param array 查找数据的数组
     * @param left 左索引
     * @param right 右索引
     * @param value 要查找的数据
     */
    private static void insertSort(List<Integer> list, int[] array,int left, int right,int value){
        //如果要查找的数据太大或者太小的话,会发生指针越界,必须加上value < array[0]和value > array[array.length-1]这两个比较
        if(left>right || value < array[0] || value > array[array.length-1]){
            return;
        }

        //自适应index,
        int index = left + (right-left)*((value-array[left])/(array[right]-array[left]));

        //如果要查找的值大于当前数组的中值,向右进行递归
        if(array[index] < value){
            insertSort(list, array, index+1, right, value);

        }
        //要查找的值小于当前数组的中值,向左进行递归
        else if(array[index] > value){
            insertSort(list,array, left,index-1,value);
        }
        //找到要匹配的值:添加到集合中,并遍历左右两边,查看是否还有其他相同数据
        else{
            list.add(index);
            //向左进行找
            int current = index-1;
            while (current>=0){
                //没有找到,跳出循环
                if(array[current]!=value){
                    break;
                }
                //找到,放到集合中
                list.add(current);
                current--;
            }

            //向右进行找
            current = index+1;
            while (current<array.length){
                //没有找到,跳出循环
                if(array[current]!=value){
                    break;
                }
                //找到,放到集合中
                list.add(current);
                current++;
            }
        }
    }
}

④ 插值查找算法的注意事项:

1)对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找速度较快

2)关键字分布不均匀的情况下,该方法不一定比折半查找要好

5)斐波那契查找算法

① 斐波那契查找算法的介绍:

黄金分割点:

黄金分割点是指把一条线段分割为两部分,使其中一部分与全长之比等于另一部分与这部分之比。取其前三位数字的近似值是0.618。由于按此比例设计的造型十分美丽,因此称为黄金分割,也称为中外比。这是一个神奇的数字,会带来意向不大的效果。

斐波那契数列:

{1, 1, 2, 3, 5, 8, 13, 21, 34, 55 } 发现斐波那契数列的两个相邻数 的比例,无限接近黄金分割值0.618

② 斐波那契查找算法原理

斐波那契查找原理与前两种相似,仅仅改变了中间结点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分割点附近,即mid=low+F(k-1)-1(F代表斐波那契数列),如下图所示:

java查找内容并选中 java中的查找算法_算法_03

对F(k-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

类似的,每一子段也可以用相同的方式分割。但顺序表长度n不一定刚好等于F[k]-1,所以需要将原来的顺序表长度n增加至F[k]-1。这里的k值只要能使得F[k]-1恰好大于或等于n即可,由以下代码得到,顺序表长度增加后,新增的位置(从n+1到F[k]-1位置),都赋为n位置的值即可。

while(n>fib[k]-1){
	k++;
}

③ 斐波那契查找算法代码

package com.immort.dataStructure.search;

import java.util.Arrays;

/**
 * 斐波那契数列查找算法:
 *  1.创建一个斐波那契的数组
 *  2.获取到当前要排序的数组,计算当前排序数组的长度满足哪一个斐波那契值
 *  3.创建一个与当前斐波那契值相等长度的数组
 *  4.将要排序数组的数据放到该数组中,将该数组剩余空间全部填上排序数组的最大值
 *  5.开始进行查找:
 *      5.1 获取第黄金分割点【循环判断】
 *          5.1.1 判断当前分割点上的数据与要查找数据的大小
 *              如果当前分割点上的数据大于要查找的数据,向左查找,寻找上一个斐波那契的值
 *              如果当前分割点上的数据小于要查找的数据,向右查找,寻找上两个斐波那契的值
 *              如果当前分割点上的数据等于要查找的数据,
 *                  比较当前mid值和high值,
 *                  如果mid值大于high值,返回high值
 *                  如果mid值小于high值,返回mid值
 *      5.2 left值大于了right的值,没有找到该数据
 * @author ppmy
 * @create 2021-04-02 19:58
 */
public class Study3FibonacciSearchDemo1 {
    private static final int MAXSIZE = 20;
    public static void main(String[] args) {
        int[] array = {1,2,3,4,6,7,8,9};
        int i = fibSearch(array, 5);
        System.out.println("index:"+i);
    }

    //先获取一个斐波那契数组
    //非递归方法创建斐波那契数组
    private static int[] fib(){
        //创建最大容量为MAXSIZE的数组
        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 array 查找数据的数组
     * @param key 需要查找的数据
     * @return 返回对应的下标
     */
    private static int fibSearch(int[] array, int key){
        //左索引
        int low = 0;
        //右索引
        int high = array.length - 1;
        //表示斐波那契分割数值的下标
        int k = 0;
        //存放mid值
        int mid = 0;
        //获取斐波那契数组
        int[] f = fib();

        //获取到斐波那契分割数值的下标
        while (high > f[k]-1){
            k++;
        }

        //因为f[k]可能大于a的长度,使用arrays构建一个新的数组,指向a[]
        int[] temp = Arrays.copyOf(array,f[k]);

        //当前数组不足的部分需要使用array数组最后的数据进行填充
        for (int i = high+1; i < temp.length; i++){
            temp[i] = array[high];
        }

        //使用while来循环处理,找到对应的斐波那契的key
        while (low <= high){
            mid = low + f[k-1] - 1;
            if(key < temp[mid]){//向数组的左边进行查找
                //更改high的值
                high = mid - 1;

                //全部的元素 = 前面的元素 + 后面的元素
                //  f[k] = f[k-1] + f[k-2]
                //  前面的f[k-1]个元素,可以继续拆分为f[k-1] = f[k-2] + f[k-3]
                //  在f[k-1]的前半部分继续查找k--
                //  下次循环的时候,mid = f[k-1-1] - 1;
                k--;
            }else if(key > temp[mid]){
                //更改low的值
                low = mid + 1;

                //  全部的元素 = 前面的元素 + 后面的元素
                //  f[k] = f[k-1] + f[k-2]
                //  后面的f[k-2]个元素,可以继续拆分为f[k-2] = f[k-3] + f[k-4]
                //  在f[k-1]的后半部分继续查找k-=2
                //  下次循环的时候,mid = f[k-1-2] - 1;
                k -= 2;
            }else{//找到了
                //需要确定的,需要返回的是哪一个下标
                if(mid <= high){
                    return mid;
                }else{
                    return high;
                }
            }
        }

        return -1;

    }
}

希望本篇文章对大家有所帮助,后续会继续分享java数据结构与算法相关学习知识…