黑马Java零基础视频教程精华部分_15_基本查找/顺序查找、二分查找/折半查找、插值查找、斐波那契查找、分块查找、哈希查找

news2024/9/23 9:30:13

系列文章目录


文章目录

  • 系列文章目录
  • 一、基本查找/顺序查找
    • 核心思想:从0索引开始挨个往后查找
    • 代码:
    • 练习:定义一个方法利用基本查找,查询某个元素在数组中的索引,数组包含重复数据。
  • 二、二分查找/折半查找
    • 核心思想:属于有序查找算法。用给定值先与中间结点比较,每次排除一半的查找范围。比较完之后有三种情况:
    • 代码:
  • 三、插值查找(二分查找的改进)
    • 核心思想:基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。当然,差值查找也属于有序查找。
  • 四、斐波那契查找(二分查找的改进)
    • 黄金分割
    • 核心思想:也是二分查找的一种提升算法,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率。同样地,斐波那契查找也属于一种有序查找算法。
    • 代码:
  • 五、分块查找
    • 核心思想:先确定要查找的元素在哪一块,然后在块内挨个查找
    • 分块查找的过程:
    • 代码:
    • 扩展的分块查找(无规律的数据)
    • 扩展的分块查找(查找的过程中还需要添加数据)
  • 六、哈希查找 即上面的扩展
  • 七、树表查找 以后补充


一、基本查找/顺序查找

核心思想:从0索引开始挨个往后查找

代码:

public class SearchWays {
    public static void main(String[] args) {
        int[] arr ={ 123,213,124,354,465,7566};
        System.out.println(basicSearch(arr, 213));

    }

    private static boolean basicSearch(int[] arr, int num){
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]==num) return true;
        }
        return  false;
    }
}

练习:定义一个方法利用基本查找,查询某个元素在数组中的索引,数组包含重复数据。

public class SearchWays {
    public static void main(String[] args) {
        int[] arr ={ 123,213,124,354,465,7566,123,123,213};
        System.out.println(basicSearch(arr, 213));

    }

    private static ArrayList<Integer> basicSearch(int[] arr, int num){
        ArrayList<Integer> nums = new ArrayList<>();
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]==num) nums.add(i);
        }
        return  nums;
    }
}

二、二分查找/折半查找

前提条件:元素必须是有序的,从小到大,或者从大到小都是可以的。
如果是无序的,也可以先进行排序。但是排序之后,会改变原有数据的顺序,查找出来元素位置跟原来的元素可能是不一样的,所以排序之后再查找只能判断当前数据是否在容器当中,返回的索引无实际的意义。

核心思想:属于有序查找算法。用给定值先与中间结点比较,每次排除一半的查找范围。比较完之后有三种情况:

  1. 相等:说明找到了
  2. 要查找的数据比中间节点小:说明要查找的数字在中间节点左边
  3. 要查找的数据比中间节点大:说明要查找的数字在中间节点右边

代码:

public class SearchWays {
    public static void main(String[] args) {
        int[] arr ={ 1,2,3,4,5,5,6,7,8,213};
        System.out.println(binarySearch(arr, 5));

    }

    private static ArrayList<Integer> binarySearch(int[] arr, int num){
        ArrayList<Integer> nums = new ArrayList<>();
        int left=0,right=arr.length-1;
        int mid=(left+right)/2;
        while(left<=right){
            if(arr[mid]==num){
                nums.add(1);
                return nums;
            } else if (arr[mid]>num) {
                right=mid-1;
                mid = (left+right)/2;
            }else {
                left=mid+1;
                mid = (left+right)/2;
            }
        }
        return  nums;
    }
}

三、插值查找(二分查找的改进)

在介绍插值查找之前,先考虑一个问题:
为什么二分查找算法一定要是折半,而不是折四分之一或者折更多呢?
其实就是因为方便,简单,但是如果我能在二分查找的基础上,让中间的mid点,尽可能靠近想要查找的元素,那不就能提高查找的效率了吗?
二分查找中查找点计算如下:

mid=(low+high)/2, 即mid=low+1/2*(high-low);

我们可以将查找的点改进为如下:

mid=low+(key-a[low])/(a[high]-a[low])*(high-low)

这样,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。

核心思想:基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。当然,差值查找也属于有序查找。

细节:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。
代码跟二分查找类似,只要修改一下mid的计算方式即可。

四、斐波那契查找(二分查找的改进)

在介绍斐波那契查找算法之前,我们先介绍一下很它紧密相连并且大家都熟知的一个概念——黄金分割。

黄金分割

  1. 黄金比例又称黄金分割,是指事物各部分间一定的数学比例关系,即将整体一分为二,较大部分与较小部分之比等于整体与较大部分之比,其比值约为1:0.618或1.618:1。
  2. 0.618被公认为最具有审美意义的比例数字,这个数值的作用不仅仅体现在诸如绘画、雕塑、音乐、建筑等艺术领域,而且在管理、工程设计等方面也有着不可忽视的作用。因此被称为黄金分割。
  3. 在数学中有一个非常有名的数学规律:斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….(从第三个数开始,后边每一个数都是前两个数的和)。
  4. 然后我们会发现,随着斐波那契数列的递增,前后两个数的比值会越来越接近0.618,利用这个特性,我们就可以将黄金比例运用到查找技术中。
    在这里插入图片描述

核心思想:也是二分查找的一种提升算法,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率。同样地,斐波那契查找也属于一种有序查找算法。

斐波那契查找也是在二分查找的基础上进行了优化,优化中间点mid的计算方式即可

代码:

public class FeiBoSearchDemo {
    public static int maxSize = 20;

    public static void main(String[] args) {
        int[] arr = {1, 8, 10, 89, 1000, 1234};
        System.out.println(search(arr, 1234));
    }

    public static int[] getFeiBo() {
        int[] arr = new int[maxSize];
        arr[0] = 1;
        arr[1] = 1;
        for (int i = 2; i < maxSize; i++) {
            arr[i] = arr[i - 1] + arr[i - 2];
        }
        return arr;
    }

    public static int search(int[] arr, int key) {
        int low = 0;
        int high = arr.length - 1;
        //表示斐波那契数分割数的下标值
        int index = 0;
        int mid = 0;
        //调用斐波那契数列
        int[] f = getFeiBo();
        //获取斐波那契分割数值的下标
        while (high > (f[index] - 1)) {
            index++;
        }
        //因为f[k]值可能大于a的长度,因此需要使用Arrays工具类,构造一个新法数组,并指向temp[],不足的部分会使用0补齐
        int[] temp = Arrays.copyOf(arr, f[index]);
        //实际需要使用arr数组的最后一个数来填充不足的部分
        for (int i = high + 1; i < temp.length; i++) {
            temp[i] = arr[high];
        }
        //使用while循环处理,找到key值
        while (low <= high) {
            mid = low + f[index - 1] - 1;
            if (key < temp[mid]) {//向数组的前面部分进行查找
                high = mid - 1;
                /*
                  对k--进行理解
                  1.全部元素=前面的元素+后面的元素
                  2.f[k]=k[k-1]+f[k-2]
                  因为前面有k-1个元素没所以可以继续分为f[k-1]=f[k-2]+f[k-3]
                  即在f[k-1]的前面继续查找k--
                  即下次循环,mid=f[k-1-1]-1
                 */
                index--;
            } else if (key > temp[mid]) {//向数组的后面的部分进行查找
                low = mid + 1;
                index -= 2;
            } else {//找到了
                //需要确定返回的是哪个下标
                if (mid <= high) {
                    return mid;
                } else {
                    return high;
                }
            }
        }
        return -1;
    }
}

五、分块查找

一般我们遇到数据没有任何顺序(使用基本查找),或者遇到数据有一定顺序(使用二分、插值、斐波那契查找)的情况都不多,一般遇到的都是无序当中又透露着有序,这时候需要使用分块查找。如下图所示,块内无序,块与块之间有序。
在这里插入图片描述
分块的原则1:前一块中的最大数据,小于后一块中所有的数据(块内无序,块间有序。
分块的原则2:块数数量一般等于数字的个数开根号。比如:16个数字般分为4块左右

核心思想:先确定要查找的元素在哪一块,然后在块内挨个查找

我们需要定义一个类来存放每个块的一些数据,如下图所示:
在这里插入图片描述
然后把块放到索引表中,如下图所示:
在这里插入图片描述
索引表具体如下图所示:可以用二分查找或者基本查找找到目标数字在哪一块中。
在这里插入图片描述

分块查找的过程:

  1. 需要把数据分成N多小块,块与块之间不能有数据重复的交集。
  2. 给每一块创建对象单独存储到数组当中
  3. 查找数据的时候,先在数组查,当前数据属于哪一块
  4. 再到这一块中顺序查找

代码:

package com.itheima.search;

public class A03_BlockSearchDemo {
    public static void main(String[] args) {
        /*
            分块查找
            核心思想:
                块内无序,块间有序
            实现步骤:
                1.创建数组blockArr存放每一个块对象的信息
                2.先查找blockArr确定要查找的数据属于哪一块
                3.再单独遍历这一块数据即可
        */
        int[] arr = {16, 5, 9, 12,21, 18,
                     32, 23, 37, 26, 45, 34,
                     50, 48, 61, 52, 73, 66};

        //创建三个块的对象
        Block b1 = new Block(21,0,5);
        Block b2 = new Block(45,6,11);
        Block b3 = new Block(73,12,17);

        //定义数组用来管理三个块的对象(索引表)
        Block[] blockArr = {b1,b2,b3};

        //定义一个变量用来记录要查找的元素
        int number = 37;

        //调用方法,传递索引表,数组,要查找的元素
        int index = getIndex(blockArr,arr,number);

        //打印一下
        System.out.println(index);



    }

    //利用分块查找的原理,查询number的索引
    private static int getIndex(Block[] blockArr, int[] arr, int number) {
        //1.确定number是在那一块当中
        int indexBlock = findIndexBlock(blockArr, number);

        if(indexBlock == -1){
            //表示number不在数组当中
            return -1;
        }

        //2.获取这一块的起始索引和结束索引   --- 30
        // Block b1 = new Block(21,0,5);   ----  0
        // Block b2 = new Block(45,6,11);  ----  1
        // Block b3 = new Block(73,12,17); ----  2
        int startIndex = blockArr[indexBlock].getStartIndex();
        int endIndex = blockArr[indexBlock].getEndIndex();

        //3.遍历
        for (int i = startIndex; i <= endIndex; i++) {
            if(arr[i] == number){
                return i;
            }
        }
        return -1;
    }


    //定义一个方法,用来确定number在哪一块当中
    public static int findIndexBlock(Block[] blockArr,int number){ //100


        //从0索引开始遍历blockArr,如果number小于max,那么就表示number是在这一块当中的
        for (int i = 0; i < blockArr.length; i++) {
            if(number <= blockArr[i].getMax()){
                return i;
            }
        }
        return -1;
    }



}

class Block{
    private int max;//最大值
    private int startIndex;//起始索引
    private int endIndex;//结束索引


    public Block() {
    }

    public Block(int max, int startIndex, int endIndex) {
        this.max = max;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    }

    /**
     * 获取
     * @return max
     */
    public int getMax() {
        return max;
    }

    /**
     * 设置
     * @param max
     */
    public void setMax(int max) {
        this.max = max;
    }

    /**
     * 获取
     * @return startIndex
     */
    public int getStartIndex() {
        return startIndex;
    }

    /**
     * 设置
     * @param startIndex
     */
    public void setStartIndex(int startIndex) {
        this.startIndex = startIndex;
    }

    /**
     * 获取
     * @return endIndex
     */
    public int getEndIndex() {
        return endIndex;
    }

    /**
     * 设置
     * @param endIndex
     */
    public void setEndIndex(int endIndex) {
        this.endIndex = endIndex;
    }

    public String toString() {
        return "Block{max = " + max + ", startIndex = " + startIndex + ", endIndex = " + endIndex + "}";
    }
}

扩展的分块查找(无规律的数据)

在这里插入图片描述
如上图所示,最小值“7”在中间,那么无论如何也无法向上面一样分块了。
我们只能退而求其次,让每一块无交集即可,如下图所示。
在这里插入图片描述
在这里插入图片描述

扩展的分块查找(查找的过程中还需要添加数据)

需求:在1~1000之间获取100个随机数,要求数据不重复。之前你每生成一个数据,就需要与已生成的(无顺序)数据进行比较,防止重复,效率很低。
改进方法,我把100个随机数分成10块,如下图所示:生成205时候只需要从第3块上查找是否重复即可。
在这里插入图片描述

  • 数组的0索引处存储1~100
  • 数组的1索引处存储101~200
  • 数组的2索引处存储201~300
  • 以此类推

但是实际上,我们一般不会采取这种方式,因为这种方式容易导致一块区域添加的元素过多,导致效率偏低。
更多的是先计算出当前数据的哈希值,用哈希值跟数组的长度进行计算,计算出应存入的位置,再挂在数组的后面形成链表,如果挂的元素太多而且数组长度过长,我们也会把链表转化为红黑树,进一步提高效率。

六、哈希查找 即上面的扩展

哈希查找是分块查找的进阶版,适用于数据一边添加一边查找的情况。
一般是数组 + 链表的结合体或者是数组+链表 + 红黑树的结合体。

七、树表查找 以后补充

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

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

相关文章

LVS多模式集群攻略!

NAT模式下的lvs集群 lvs-nat概念&#xff1a;修改请求报文的目标IP,多目标IP的DNAT&#xff0c;本质是多目标IP的DNAT&#xff0c;通过将请求报文中的目标地址和目标端口修改为某挑出的RS的RIP和PORT实现转发 最终实现效果&#xff1a; 1.Director 服务器采用双网卡&#xff…

Qt入门(二):第一个Qt项目

新建项目 打开Qt Creator&#xff0c;新建项目&#xff0c;然后一路next 到这一步baseclass有三种选择&#xff1a; QMainWindow&#xff1a;主窗口基类&#xff0c;相较于QWidget&#xff0c;多了菜单栏等杂七杂八的东西。QWidget&#xff1a;最基础的窗口基类&#xff0…

编译运行 Byconity

我的系统是centos&#xff0c;因此用他们的docker编译并用他们的docker-compose运行&#xff0c;以下流程亲测可跑&#xff1a; 拉取并编译 https://github.com/ByConity/ByConity/tree/master/docker/debian/dev-env 运行 https://github.com/ByConity/ByConity/blob/master/d…

Matplotlib | 一文搞定Matplotlib从入门到实战演练!

文章目录 1 什么是Matplotlib1.1 Matplotlib的安装1.2 Matplotlib的基本使用 2 绘制直线3 绘制折线设置标签文字和线条粗细设置中文标题风格的设置 4 绘制曲线绘制曲线yx^2绘制正弦曲线和余弦曲线画布分区 5 绘制散点图绘制不同种类不同颜色的线 6 绘制条形图&#xff08;柱状&…

代码之外的生存指南——生产力

自己感觉今天都没有喝一口水的时间忙忙碌碌的工作了一天&#xff0c;但是到快下班或晚上回想一下今天自己到底在忙些什么的时候&#xff0c;却好像也没有做些什么对自己工作主线有意义的事情&#xff0c;大多时候时间都花费在了那些检查邮件、泡茶、拨打电话、开会议等干扰内容…

科普文:业务场景之常见10家HIS厂商概叙

智慧医院信息化经过几十年的发展&#xff0c;涌现出了一大批优秀的建设企业&#xff0c;我们选取了市面上部分主流的HIS厂商进行了汇集&#xff0c;包括厂商的发展情况、产品情况、技术情况、案例情况等。 卫宁健康 WINEX 口号&#xff1a;软件认知医疗 最新产品&#xff1a;…

集智书童 | 浙江大学 蚂蚁集团提出 PAI,一种无需训练减少 LVLM 幻觉的方法 !

本文来源公众号“集智书童”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;浙江大学 & 蚂蚁集团提出 PAI&#xff0c;一种无需训练减少 LVLM 幻觉的方法 &#xff01; 浙江大学 & 蚂蚁集团提出 PAI&#xff0c;一种无需训…

BERT预训练

一、动机 1、在NLP中的迁移学习中&#xff0c;使用预训练好的模型抽取词、句子的特征&#xff0c;不更新预训练好的模型&#xff0c;而是在需要构建新的网络来抓取新任务需要的信息&#xff0c;也就是最后面加上一个MLP做分类&#xff1b; 2、由于基于微调的NLP模型&#xff…

21. 合并两个有序链表(递归)

目录 一;题目&#xff1a; 二代码; 三&#xff1a;结果&#xff1a; 一;题目&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 二代码; /*** Definition for singly-linked list.* struct ListNode {* …

HTMX 和 FastAPI 绝佳搭配

FastAPI的优势 FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于基于标准 Python 类型提示使用 Python 3.7 构建 API。以下是它的一些主要优点&#xff1a; 性能&#xff1a;FastAPI 基于 Starlette 和 Pydantic 构建&#xff0c;使其与 …

Linux 中 core dump 异常的分析

目录 一、概述二、发生 core dump 的原因1. 空指针或非法指针2. 数组越界或指针越界3. 数据竞争 三、分析 core dump 的方法1. 启用 core dump2. 触发 core dump2.1 因空指针解引用而崩溃2.2 通过 信号触发 core dump 3. 利用 gdb 分析 core dump 一、概述 在 UNIX 系统中&…

sqli-labs第一关详细解答

首先判断是否有注入点 发现and 11 和 and 12结果一样&#xff0c;所以应该是字符型注入&#xff0c;需要对单引号做闭合 做闭合后发现报错&#xff0c;提示Limit 0,1&#xff0c;那就说明存在注入点&#xff0c;但是要注释掉后面的limit 0,1 使用--注释掉limit 0,1后&#xff…

25考研英语长难句Day05

25考研英语长难句Day05 【词组】【断句】 【词组】 单词解释gelimpsen.一瞥、瞥见rapidly adv.迅速&#xff1b;迅速地&#xff1b;高速&#xff1b;急速地&#xff1b;急促scene n.场景&#xff1b;&#xff08;尤指不愉快事件发生的)地点&#xff0c;现场&#xff1b;场面&a…

记录下泡面神器的满血复活-Kindle Voyage刷安卓系统记录

Kindle在国内已经没有服务了&#xff0c;一段时间内通过连手机热点(上下班通勤)&#xff0c;用内置浏览器访问微信读书&#xff0c;但体验不是很好&#xff0c;在考虑是否购买一个国内的墨水屏阅读器时&#xff0c;偶然想到了是否可以刷安卓&#xff0c;然后装微信读书的墨水屏…

超详细!网络安全知识入门及学习流程

第一章&#xff1a;网络安全的基本概念和术 一、网络安全的基本概念 1.保密性&#xff08;Confidentiality&#xff09; 定义&#xff1a;确保信息在存储、传输和处理过程中不被未授权的人员访问或获取。例子&#xff1a;企业的商业机密文件被加密存储&#xff0c;只有拥有正…

5个理由让你爱上CleanMyMac2025告别卡顿,迎接极速体验!

CleanMyMac是一款Mac电脑专用的清理工具&#xff0c;具有系统垃圾、大型旧文件、邮件附件、iTunes垃圾、软件卸载残余等清理功能。 它采用先进的扫描技术&#xff0c;快速识别并清除垃圾文件&#xff0c;释放磁盘空间&#xff0c;提高系统运行速度。 同时&#xff0c;它还具备…

Android经典实战之Kotlin中实现圆角图片和圆形图片

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 实现圆角是一个很常见的需求&#xff0c;也有很多种方式&#xff0c;这里介绍2种&#xff0c;实现起来都不麻烦&#xff0c;很方便 方法一&…

JS+CSS案例:可适应上下布局和左右布局的菜单(含二级菜单)

今天,我给大家分享一个原创的CSS菜单,整个菜单全由CSS写成,仅在切换布局时使用JS。合不合意,先看看效果图。 本例图片 接下来,我来详细给大家分享它的制作方法。 文件夹结构 因为涉及到了样式表切换,所以,你需要借鉴一下我的文件夹结构。 CSS文件夹: reset.css 用于…

【Dash】Dash Layout

一、Dash Layout Dash apps are composed of two parts. The first part is the layout, which describes what the app looks like. The second part describes the interactivity of the app. To get started, create a file named app.py, copy the code below into it, a…

Linux权限-chmod命令

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 根据前面Linux用户介绍&#xff0c;里面涉及到超级管理员&#xff0c;普通用户&#xff0c;系统用户&#xff0c;既然用户有…