第八章 数据结构与算法(查找算法)

2021/5/13 20:55:34

本文主要是介绍第八章 数据结构与算法(查找算法),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

在java中,我们常用的查找算法有四种:
(1)顺序(线性)查找
(2)二分查找/折本查找
(3)插值查找
(4)斐波那契查找

8.1线性查找算法
有一个数列:{1,8,19,3,1000},判断数列中是否包含此名称【顺序查找】要求:如果找到了,就提示找到,并给出下标值
@思路:
遍历如果找到符合条件的值就返回

public class SeqSearch {
    public static void main(String[] args) {
        int[] arr={23,1,78,9,55};
        int index=seqSearch(arr,78);
        if (index != -1){
            System.out.println("目标值的下标为:"+index);
        }else {
            System.out.println("没有找到目标值");
        }
    }

    /**
     * 此方法为找到一个目标值就返回
     * @param arr
     * @param value
     * @return
     */
    private static int seqSearch(int[] arr, int value) {
        for (int i=0;i<arr.length;i++){
            if (arr[i] == value){
                return i;
            }
        }
        return -1;
    }
}

8.2二分查找算法
ps:二分查找是对有序数组进行查找
8.2.1二分查找思路分析
在这里插入图片描述
8.2.2实例
(1)请对一个有序数组进行二分查找 {1,8, 10, 89, 1000, 1234} ,输入一个数看看该数组是否存在此数,并且求出下标,如果没有就提示"没有这个数"。
(2)说明:增加了找到所有的满足条件的元素下标:
课后思考题: {1,8, 10, 89, 1000, 1000,1234} 当一个有序数组中,有多个相同的数值时,如何将所有的数值都查找到,比如这里的 1000.

//二分查找的前提是数组是有序的
public class BinarySort {
    public static void main(String[] args) {
        int[] arr={1,2,2,2,34,67,88,123};
        List<Integer> integers = binarySort2(arr, 0, arr.length - 1, 2);
        Collections.sort(integers);  //对list集合中元素进行排序后输出
        System.out.println(integers); //(list集合特点:有序可重复指的是输出顺序与插入顺序一致,不是数字之间的大小顺序)
        /*int i = binarySort(arr, 0, arr.length - 1, 2);
        System.out.println(i);*/
    }

    /**二分查找(此方法参数一定要传入左右下标,因为需要递归进行局部遍历,左右下标会动态变化)
     * @param arr  传入待查找数组
     * @param left  待比较数组的左下标
     * @param right  待比较数组的右下标
     * @param findVal 待查找值
     */
    public static int binarySort(int[] arr,int left,int right,int findVal){
         if(right < left){  //遍历完数组,没有找到符合值
             return -1;
         }
         int mid=(left+right)/2;  //求出中间值下标
        int midValue=arr[mid];  //求出中间值
        if (findVal >midValue){  //比中间值大,向右递归
            return binarySort(arr,mid+1,right,findVal);
        }else if (findVal < midValue){
            return binarySort(arr,left,mid-1,findVal);
        }else {
            return mid;  //找到
        }
    }

    /**增加条件:查找所有满足条件的元素坐标
     * @param arr
     * @param left
     * @param right
     * @param findVal
     * @return
     */
    public static List<Integer> binarySort2(int[] arr, int left, int right, int findVal){
        ArrayList<Integer> indexs = new ArrayList<>();
        if(right < left){  //遍历完数组,没有找到符合值
            return indexs;
        }
        int mid=(left+right)/2;  //求出中间值下标
        int midValue=arr[mid];  //求出中间值
        if (findVal >midValue){  //比中间值大,向右递归
            return binarySort2(arr,mid+1,right,findVal);
        }else if (findVal < midValue){
            return binarySort2(arr,left,mid-1,findVal);
        }else {
            /*课后思考题: {1,8, 10, 89, 1000, 1000,1234} 当一个有序数组中,
             * 有多个相同的数值时,如何将所有的数值都查找到,比如这里的 1000
             * 思路分析
             * 1. 在找到 mid 索引值,不要马上返回
             * 2. 向 mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合 ArrayList
             * 3. 向 mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合 ArrayList
             * 4. 将 Arraylist 返回*/
            //向左查找
            int i=mid-1;
            while (true){
                if (i<0 || arr[i] != findVal){  //因为二分查找法是针对有序的数,如果midVal值符合,
                    break;            //则其他几个数也在临近的位置(要么就查找完毕要么就没有了退出循环),
                }                       //所以后面的阶数条件是arr[i] !=midVal
                indexs.add(i);
                i--;
            }
            indexs.add(mid);  //此时的中间值符合别忘了放入集合中
            //向右查找
             i=mid+1;
            while (true){
                if (i>arr.length-1 || arr[i] != findVal){
                    break;
                }
                indexs.add(i);
                i++;
            }
            return indexs;  //返回集合
        }
    }

}

8.3插值查找算法
(1)插值查找原理介绍:插值查找算法类似于二分查找,不同的是插值查找每
在这里插入图片描述
8.3.1插值查找应用实例
@请对一个有序数组进行插值查找 ,输入一个数看看该数组是否存在此数,并且求出下标,如果没有就提示"没有这个数"。

//插值查找也是适应有序的数组
public class InsertValueSort {
    public static void main(String[] args) {
        int[] arr = new int[100];
        for (int i=0;i<100;i++){
            arr[i]=i;
        }
        int i = insertValueSort(arr, 0, arr.length - 1, 10);
        System.out.println(i);
    }

    /**
     * @param arr  待查找数组
     * @param left  待查找数组的左边边界索引
     * @param right  待查找数组的右边边界索引
     * @param findNum 待查找的数
     * @return
     */
    public static int insertValueSort(int[] arr,int left,int right,int findNum){
        //System.out.println("----");
        //findNum<arr[0] || findNum>arr[arr.length-1]条件不仅是优化,而且是必要条件
        //防止后面的arr[mid]数组越界(倘若findNum非常大)
        if (left>right || findNum<arr[0] || findNum>arr[arr.length-1]){
            return -1;
        }
        //插值算法求mid
        int mid=left+(right-left)*(findNum-arr[left])/(arr[right]-arr[left]);
        int midValue=arr[mid];
        if (findNum<midValue){  //如果小于就往左边循环
            return insertValueSort(arr,left,mid-1,findNum);
        }else if (findNum>midValue){
            return insertValueSort(arr,mid+1,right,findNum);
        }else {
            return mid;
        }
    }
}

8.4斐波拉契(黄金分割法)查找算法
8.4.1斐波拉契查找基本介绍
(1)黄金分割点是指把一条线段分割为两部分,使其中一部分与全长之比等于另一部分与这部分之比。取其前三位数字的近似值是 0.618。由于按此比例设计的造型十分美丽,因此称为黄金分割,也称为中外比。这是一个神奇的数字,会带来意向不大的效果。
(2) 斐波那契数列 {1, 1, 2, 3, 5, 8, 13, 21, 34, 55 } 发现斐波那契数列的两个相邻数 的比例,无限接近 黄金分割值0.618
8.4.2斐波拉契原理
斐波那契查找原理与前两种相似,仅仅改变了中间结点(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 即可,由以下代码得到,顺序表长度增加后,新增的位置(从 n+1 到 F[k]-1 位置),都赋为 n 位置的值即可。

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

8.4.3斐波拉契查找应用实例:
@请对一个有序数组进行斐波那契查找 {1,8, 10, 89, 1000, 1234} ,输入一个数看看该数组是否存在此数,并且求出下标,如果没有就提示"没有这个数"。

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, 189));// 0
    }

    //因为后面我们 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] 值 可能大于 a 的 长度,因此我们需要使用 Arrays 类,构造一个新的数组,并指向 temp[]
        //不足的部分会使用 0 填充
        int[] temp = Arrays.copyOf(a, f[k]);
         //实际上需求使用 a 数组最后的数填充 temp
        //举例:
        //temp = {1,8, 10, 89, 1000, 1234, 0, 0} => {1,8, 10, 89, 1000, 1234, 1234, 1234,}
        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] = f[k-2] + f[k-3]
                //即 在 f[k-1] 的前面继续查找 k--
                //即下次循环 mid = f[k-1-1]-1
                k--;
            } else if ( key > temp[mid]) { // 我们应该继续向数组的后面查找(右边)
                low = mid + 1;
            //为什么是 k -=2
            //说明
            //1. 全部元素 = 前面的元素 + 后边元素
            //2. f[k] = f[k-1] + f[k-2]
            //3. 因为后面我们有 f[k-2] 所以可以继续拆分 f[k-1] = f[k-3] + f[k-4]
            //4. 即在 f[k-2] 的前面进行查找 k -=2
            //5. 即下次循环 mid = f[k - 1 - 2] - 1
                k -= 2;
            } else { //找到
            //需要确定,返回的是哪个下标
                if(mid <= high) {
                    return mid;
                } else {
                    return high;
                }
            }
        }
        return -1;
    }
}



这篇关于第八章 数据结构与算法(查找算法)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程