【尚硅谷】Java数据结构与算法笔记08 - 查找算法

news2025/1/18 7:22:51

文章目录

  • 一、查找算法介绍
  • 二、线性查找算法
  • 三、二分查找算法
    • 3.1 思路分析
    • 3.2 代码实现
  • 四、插值查找算法
    • 4.1 思路分析
    • 4.2 代码实现
    • 4.3 注意事项
  • 五、斐波那契(黄金分割法)查找算法
    • 5.1 思路分析
    • 5.2 原理讲解
    • 5.3 代码实现


一、查找算法介绍

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


二、线性查找算法

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

代码实现:

public class SeqSearch {
    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);
        }
    }

    /**
     * 这里我们实现的线性查找是找到一个满足条件的值,就返回
     *
     * @param arr
     * @param value
     * @return
     */
    public static int seqSearch(int[] arr, int value) {
        // 线性查找是逐一比对,发现有相同值,就返回下标
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == value) {
                return i;
            }
        }
        return -1;
    }
}

三、二分查找算法

二分查找的使用前提是:所要查找的数组是有序的

3.1 思路分析

在这里插入图片描述

3.2 代码实现

public class BinarySearch {
    public static void main(String[] args) {
        int arr[] = {1, 8, 10, 89, 1000, 1000, 1234};
        List<Integer> resIndexList = binarySearch(arr, 0, arr.length - 1, 1000);
        System.out.println("resIndexList=" + resIndexList);
    }

    // 二分查找算法
    //完成一个课后思考题:
    /*
     * 课后思考题: {1,8, 10, 89, 1000, 1000,1234} 当一个有序数组中,
     * 有多个相同的数值时,如何将所有的数值都查找到,比如这里的 1000
     *
     * 思路分析
     * 1. 在找到mid 索引值,不要马上返回
     * 2. 向mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
     * 3. 向mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
     * 4. 将Arraylist返回
     */

    /**
     * @param arr     数组
     * @param left    左边的索引
     * @param right   右边的索引
     * @param findVal 要查找的值
     * @return 如果找到就返回下标集合,如果没有找到,就返回 []
     */
    public static List<Integer> binarySearch(int[] arr, int left, int right, int findVal) {
        // 当 left > right 时,说明递归整个数组,但是没有找到
        if (left > right) {
            return new ArrayList<Integer>();
        }
        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 {
//			 * 思路分析
//			 * 1. 在找到mid 索引值,不要马上返回
//			 * 2. 向mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
//			 * 3. 向mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
//			 * 4. 将Arraylist返回
            List<Integer> resIndexlist = new ArrayList<Integer>();
            //向mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
            int temp = mid - 1;
            while (true) {
                if (temp < 0 || arr[temp] != findVal) {//退出
                    break;
                }
                //否则,就temp 放入到 resIndexlist
                resIndexlist.add(temp);
                temp -= 1; //temp左移
            }
            resIndexlist.add(mid);  //

            //向mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
            temp = mid + 1;
            while (true) {
                if (temp > arr.length - 1 || arr[temp] != findVal) {//退出
                    break;
                }
                //否则,就temp 放入到 resIndexlist
                resIndexlist.add(temp);
                temp += 1; //temp右移
            }
            return resIndexlist;
        }
    }

}

四、插值查找算法

4.1 思路分析

1)插值查找原理介绍:
插值查找算法类似于二分查找, 不同的是插值查找每次从自适应 mid 处开始查找。
2)将折半查找中的求 mid 索引的公式, low 表示左边索引 left, high 表示右边索引 right. key 就是前面我们讲的 findVal

在这里插入图片描述
3)

在这里插入图片描述

4)举例说明插值查找算法 1-100 的数组

在这里插入图片描述

4.2 代码实现

public class InsertValueSearch {
    public static void main(String[] args) {
        int arr[] = {1, 8, 10, 89, 1000, 1000, 1234};
        int index = insertValueSearch(arr, 0, arr.length - 1, 1234);
        System.out.println("index = " + index);
    }

    //编写插值查找算法
    //说明:插值查找算法,也要求数组是有序的

    /**
     * @param arr     数组
     * @param left    左边索引
     * @param right   右边索引
     * @param findVal 查找值
     * @return 如果找到,就返回对应的下标,如果没有找到,返回-1
     */
    public static int insertValueSearch(int[] arr, int left, int right, int findVal) {
        //注意:findVal < arr[0]  和  findVal > arr[arr.length - 1] 必须需要
        //否则我们得到的 mid 可能越界
        if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {
            return -1;
        }
        // 求出mid, 自适应
        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;
        }
    }

}

4.3 注意事项

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

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


五、斐波那契(黄金分割法)查找算法

5.1 思路分析

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

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

5.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]=F[k-1]+F[k-2] F[k]=F[k1]+F[k2] 的性质, 可以得到 ( F [ k ] − 1 ) = ( F [ k − 1 ] − 1 ) + ( F [ k − 2 ] − 1 ) + 1 (F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+1 (F[k]1)=(F[k1]1)+(F[k2]1)+1 。该式说明: 只要顺序表的长度为 F [ k ] − 1 F[k]-1 F[k]1, 则可以将该表分成长度为 F [ k − 1 ] − 1 F[k-1]-1 F[k1]1 F [ k − 2 ] − 1 F[k-2]-1 F[k2]1 的两段, 即如上图所示。从而中间位置为 mid=low+F(k-1)-1

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

5.3 代码实现

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));
    }

    //因为后面我们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;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/161882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++多线程入门及基础知识

什么是C多线程 线程即操作系统进行CPU任务调度的最小单位。C的多线程并发&#xff0c;简单理解的话就是&#xff0c;将任务的不同功能交由多个函数实现&#xff0c;创建多个线程&#xff0c;每个线程执行一个函数&#xff0c;一个任务就同时由不同线程执行。 什么时候使用多…

微信小程序:骨架屏的实现方法

骨架屏是为了展示一个页面骨架而不含有实际的页面内容&#xff0c;从渲染效率上来讲&#xff0c;骨架屏它并不能使首屏渲染加快。由于骨架屏的一些使用又向用户渲染了额外的一些内容&#xff0c;这些内容是额外添加的、本来是不需要渲染的&#xff0c;它反而从整体上加长了首屏…

Windows 虚拟磁盘驱动开发(采用原始办法实现类似Storport框架的相同功能)

其实以前讲述windows平台下的磁盘驱动的开发挺多&#xff0c;而且时间也是非常早。以下连接&#xff1a;https://blog.csdn.net/fanxiushu/article/details/9903123?spm1001.2014.3001.5501https://blog.csdn.net/fanxiushu/article/details/11713357?spm1001.2014.3001.5501…

游戏开发 状态同步

【状态同步】1、将所有的操作发送给Server&#xff08;T1&#xff09;&#xff0c;由Server计算&#xff08;T2&#xff09;&#xff0c;并返回结果&#xff08;T3&#xff09;。权威服务器架构能够防止很多的作弊&#xff0c;但是直接用这种方法会让游戏的响应变得迟缓。如果 …

three games 之 桌球

接下来介绍一些 Vue4 中的一些进阶使用&#xff0c;希望对大家有所帮助&#xff0c;谢谢。 如果文中有不对、疑惑的地方&#xff0c;欢迎在评论区留言指正&#x1f33b; 一、项目结构 Vuex 并不限制你的代码结构。但是&#xff0c;它规定了一些需要遵守的规则&#xff1a; …

移动硬盘怎么分区?

硬盘分区指的是硬盘上被划分出来的区块&#xff0c;可用于分类存储各种数据。而我们日常购买的移动硬盘通常来说分为两种&#xff0c;一种是买回来已分好区的&#xff0c;还有一种是未经过分区的。如果移动硬盘没有经过分区&#xff0c;那么在将它连接到电脑的USB接口时&#x…

android四大组件之四-BroadCast实现原理分析

前言&#xff1a; 一开始的目标是解决各种各样的ANR问题的&#xff0c;但是我们知道&#xff0c;ANR总体上分有四种类型&#xff0c;这四种ANR类型有三种是和四大组件相对应的&#xff0c;所以&#xff0c;如果想了解ANR发生的根因&#xff0c;对安卓四大组件的实现原理必须要…

伙伴云与飞书、金山办公一同入选亿欧2022中国数字化企业服务商TOP50

近日&#xff0c;由中关村国家自主创新示范区展示中心、中关村会展与服务产业联盟与亿欧联合举办的SHOWTECH2022-WIM 创新者年会”在京顺利召开&#xff0c;会上&#xff0c;亿欧网重磅发布《2022世界创新奖榜单》。伙伴云凭借10年来为企业数字化转型赋能的成功经验和卓越贡献&…

数据结构学习-队列

坚持看完&#xff0c;结尾有思维导图总结 这里写目录标题队列的定义于性质如何实现队列的功能初始化队列入队列出队列队列的销毁队列取队头数据队列取队尾数据判断队列是否为空判断队列长度总结队列的定义于性质 队列是一种数据结构&#xff0c;他储存数据的方式就和排队一样 …

二十六、Kubernetes中Horizontal Pod Autoscaler(HPA)控制器详解

1、概述 在kubernetes中&#xff0c;有很多类型的pod控制器&#xff0c;每种都有自己的适合的场景&#xff0c;常见的有下面这些&#xff1a; ReplicationController&#xff1a;比较原始的pod控制器&#xff0c;已经被废弃&#xff0c;由ReplicaSet替代 ReplicaSet&#xff…

年终盘点(三)丨2022计讯物联团队不负韶华,奋力前行

光阴荏苒&#xff0c;时光悄然&#xff0c;成长的齿轮不断转动。2022年&#xff0c;计讯人在挑战中创造不凡&#xff0c;2023年&#xff0c;计讯人在希望中迎接新未来。 回首过去&#xff0c;计讯物联团队不断壮大&#xff0c;在奋勇前行中以坚持书写拼搏&#xff0c;在知难而…

记好这24个ES6方法,用于解决实际开发的JS问题

本文主要介绍24中es6方法&#xff0c;这些方法都挺实用的&#xff0c;本本请记好&#xff0c;时不时翻出来看看。 1.如何隐藏所有指定的元素 1 const hide (el) > Array.from(el).forEach(e > (e.style.display none)); 2 3 // 事例:隐藏页面上所有<img>元素? …

echarts——实现 面积图+柱状图+折线图等——基础积累

因为到年底了&#xff0c;很多项目组都开始做年终汇报&#xff0c;年终汇报的展示形式最常见的就是看板。 样式美观&#xff0c;可以放到电视机或者大屏上&#xff0c;通过图表的形式进行展示&#xff0c;简单明了&#xff0c;通俗易懂。 直接上最终效果图&#xff1a;是一个…

【C++】打开C++的大门

目录前言1.什么是C2.C的发展史3.C关键字&#xff08;C98&#xff09;4.命名空间4.1命名冲突4.2命名空间定义4.3命名空间使用5.输入输出6.缺省参数6.1缺省参数的概念6.2缺省参数分类7.函数重载7.1函数重载概念7.2C函数重载的原理——名字修改8.引用8.1引用的概念8.2引用特性8.3常…

ArcGIS基础实验操作100例--实验94计算栅格图层总和值

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 空间分析篇--实验94 计算栅格图层总和值 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;…

【观察】软硬件底层创新“开花结果”,亚马逊云科技的沉淀与释放

2006年&#xff0c;亚马逊云科技推出了Amazon Web Services&#xff0c;正式“开创”出了云计算市场。同年8月&#xff0c;Amazon Elastic Compute Cloud (EC2) 开放了 beta 测试&#xff0c;启动了云上计算的创新和革命。从此&#xff0c;亚马逊云科技在云计算软硬件底层技术创…

软件测试复习03:动态测试——黑盒测试

作者&#xff1a;非妃是公主 专栏&#xff1a;《软件测试》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录等价划分法边值分析法错误推测法因果图法示例习题等价划分法 等价类&#xff1a;一个几何&#xf…

阿里云 gradle maven配置中心地址

仓库名称阿里云仓库地址阿里云仓库地址(老版)源地址centralhttps://maven.aliyun.com/repository/centralhttps://maven.aliyun.com/nexus/content/repositories/centralhttps://repo1.maven.org/maven2/jcenterhttps://maven.aliyun.com/repository/publichttps://maven.aliyu…

dp(五) 最长公共子串

最长公共子串_牛客题霸_牛客网 描述 给定两个字符串str1和str2,输出两个字符串的最长公共子串 题目保证str1和str2的最长公共子串存在且唯一。 数据范围&#xff1a; 1≤∣str1∣,∣str2∣≤50001≤∣str1∣,∣str2∣≤5000 要求&#xff1a; 空间复杂度 O(n2)O(n2)&#x…

【阶段三】Python机器学习22篇:机器学习项目实战:GBDT分类模型

本篇的思维导图: 项目实战(GBDT分类模型) 项目背景 应用GBDT算法实现多分类模型,目标是实现GBDT多分类项目的全流程。 数据获取 本次建模数据来源于网络,数据项统计如下: 编号  变量名称 <