【JAVA入门】Day23 - 查找算法

news2024/12/28 6:40:39

【JAVA入门】Day23 - 查找算法


文章目录

  • 【JAVA入门】Day23 - 查找算法
    • 一、基本查找
    • 二、二分查找 / 折半查找
    • 三、分块查找


        查找算法我们常用的有:

  • 基本查找
  • 二分查找 / 折半查找
  • 分块查找
  • 插值查找
  • 斐波那契查找
  • 树表查找
  • 哈希查找

        这里我们着重讲解前三种,其他查找方式除了树表查找,都是前三种的扩展。

一、基本查找

131 127 147 81 103 23 7 79

        在一堆数据中找出想要的数据,只需要将它们放入一个容器中,挨个遍历,直到找到想要的数据。

package BasciSeach;

public class BasicSearchDemo1 {
    public static void main(String[] args) {
        //从0索引开始挨个往后查找
        //基本查找/顺序查找

        //数据:{131, 127, 147, 81, 103, 23, 7, 79}
        //需求:是否存在
        int[] arr = {131, 127, 147, 81, 103, 23, 7, 79};
        int num = 18;
        boolean result = basicSearch(arr, num);
        System.out.println(result);
    }


    //参数:
    //一:数组
    //二:要查找的元素
    public static boolean basicSearch(int[] arr, int num) {
        //利用基本查找number是否在数组中存在
        for(int i = 0; i < arr.length; i++) {
            if(arr[i] == num){
                return true;
            }
        }
        return false;
    }
}

        利用基本查找,返回元素的索引(不考虑有无重复元素)。

package BasciSeach;

public class BasicSearchDemo2 {
    public static void main(String[] args) {
        //从0索引开始挨个往后查找
        //基本查找/顺序查找

        //数据:{131, 127, 147, 81, 103, 23, 7, 79}
        //需求:返回索引
        int[] arr = {131, 127, 147, 81, 103, 23, 7, 79};
        int num = 7;
        int index = basicSearch(arr, num);
        if(index > 0) {
            System.out.println(index);
        }else{
            System.out.println("未找到元素");
        }
    }


    //参数:
    //一:数组
    //二:要查找的元素
    public static int basicSearch(int[] arr, int num) {
        //利用基本查找number是否在数组中存在
        for(int i = 0; i < arr.length; i++) {
            if(arr[i] == num){
                return i;
            }
        }
        return -1;
    }
}

        利用基本查找,返回元素的所有索引(考虑有重复元素)。

package BasciSeach;

import java.util.ArrayList;

public class BasicSearchDemo3 {
    public static void main(String[] args) {
        //利用基本查找,返回元素的索引(考虑有重复元素)
        //返回所有索引
        int[] arr = {131, 127, 147, 81, 103, 23, 7, 81};
        int num = 81;
        ArrayList<Integer> list = basicSearch(arr, num);
        System.out.print("[");
        for(int i = 0; i < list.size(); i++) {
            if(i < list.size() - 1) {
                System.out.print(list.get(i) + ", ");
            }else{
                System.out.print(list.get(i));
            }
        }
        System.out.print("]" + "\n");
    }

    //心得:
    //如果要返回多个数据的话,可以放入集合中
    public static ArrayList<Integer> basicSearch(int[] arr, int num) {
        ArrayList<Integer> list = new ArrayList<>();
        for(int i = 0 ; i < arr.length; i++) {
            if(arr[i] == num) {
                list.add(i);
            }
        }
        return list;
    }
}

二、二分查找 / 折半查找

        二分查找的数据有前提要求:数据必须是有序的。可以是从小到大排列,也可以是从大到小排列。
        二分查找的核心逻辑就是:每次排除一半的查找范围。因此查找效率比基本查找快很多。
        定义两个变量,min 指向 0 索引,max 指向 最大索引,(max + min)/ 2 可以得到中间的索引 mid。此时看该索引和要查找的数据大小关系,如果它比要查找的数据大,那么该数据所在的范围一定在 mid 索引的左半边,此时可以直接令 max = mid - 1,缩小索引范围;如果它比要查找的数据小,那么该数据所在范围一定在 mid 索引的右半边,此时可以直接令 min = mid + 1,缩小索引范围。重复此流程,直到max == min,此时就能找到所找的数据。如果要被查找的数据不存在,程序就会继续执行刚刚的逻辑,此时会发生 min > max,循环结束。

package BasciSeach;

public class BinarySearchDemo1 {
    public static void main(String[] args) {
        //二分查找
        //{7, 23, 79, 81, 103, 127, 131, 147}
        int[] arr = {7, 23, 79, 81, 103, 127, 131, 147};
        int number = 81;
        System.out.println(binarySearch(arr, number));
    }

    public static int binarySearch(int[] arr, int number) {
        //1.定义两个变量记录要查找的范围
        int min = 0;
        int max = arr.length - 1;

        //2.利用循环不断去找要查找的数据
        while(true) {
            if(min > max) {
                System.out.println("要查找的数据不存在!");
                return -1;
            }

            //3.找到min和max的中间位置
            int mid = (min + max) / 2;

            //4.拿着mid指向的元素跟要查找的元素进行比较
            //4.1number在mid左边
            //4.2number在mid右边
            //4.3number跟mid指向的元素一样
            if(arr[mid] > number) {
                max = mid - 1;
            }else if(arr[mid] < number) {
                min = mid + 1;
            }else if(arr[mid] == number) {
                return mid;
            }
        }
    }
}

        二分查找最大的优势就是能提高查找的效率,但是它要求数据必须是有序的。
        如果数据是乱序,先排序再使用二分查找得到的索引没有任何意义,只能知道这个数据是否存在。因为排序之后,数字的位置可能就发生变化了,新的索引没有参考价值。
        二分查找的效率其实可以进一步优化,将 mid 的计算方法改进,如下公式:

mid = min + (key - arr[min]) / (arr[max] - arr[min]) * (max - min);

        其中,key 是要查找的数据。此时这种查找方式就不叫二分查找了,而是叫做插值查找
        还可以利用数学中的黄金分割点的概念进行改进:

mid = min + 黄金分割点左半边长度 - 1

        此时这种查找方法就不叫二分查找了,而是叫做斐波那契查找

三、分块查找

        如果一组数据:块间有序,块内无序。这个时候我们可以应用分块查找
        分块查找的原则在于:

  • 前一块中的最大数据,小于后一块中的所有数据(块内无序,块间有序)。
  • 块数数量一般等于数字的总个数开根号。比如:16个数字一般分为4块左右。

        块对象的定义一般如下所示:

class Block { 			//块
	int max;  			//块中的最大值
	int startIndex;		//起始索引
	int endIndex;		//结束索引
}

        将块对象放到一个数组中统一管理,这个数组一般叫做索引表。通过索引表,我们就能确定要查找的值存在于哪一块当中。
在这里插入图片描述
在这里插入图片描述
        比如上面的数据,我们可以轻易地发现,30就在第三个块当中(这个块级寻找的过程可以用基本查找或二分查找实现)。然后,我们再在块内使用遍历,就可以轻易找到要查找的数据30。

package BasciSeach;

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

        //1.把数据分块
        //分几块:总数开根号 √18 = 4.24 块左右
        //每块里 18 / 4 = 4.5 个

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

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

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

        //调用方法,传递索引表,数组,要查找的元素,返回要查找的数据索引
        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;
        }

        //获取这一块的起始索引和结束索引
        int startIndex = blockArr[indexBlock].getStartIndex();
        int endIndex = blockArr[indexBlock].getEndIndex();

        //遍历这一部分的数组
        for(int i = startIndex; i <= endIndex; i++) {
            if(arr[i] == number) {
                return i;
            }
        }
        //遍历完整块都没找到,返回-1
        return -1;
    }

    //定义一个方法,用来确定number在哪一块中
    private static int findIndexBlock(Block[] blockArr, int number) {
        /*//创建三个块的对象
        Block b1 = new Block(21,0,5);
        Block b2 = new Block(32,6,11);
        Block b3 = new Block(50,12,17);*/

        //从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;
    }

    public int getMax() {
        return max;
    }

    public int getStartIndex() {
        return startIndex;
    }

    public int getEndIndex() {
        return endIndex;
    }
}

        如果数据本身没有规律。只需要保证数据间尚无交集,就可以完成分块。我们可以新增一个 min 变量,来记录每块中最小的值。
在这里插入图片描述
        如果查找的过程中还需要添加新数据呢?常见需求:在1~1000之间获取100个随机数,要求这些数据不重复,那就需要每添加一个数据就进行一次查找,查看该数据是否已经存在。这个时候就可以利用分块查找,把这100个数据分成10块,每一块规定要存储数据的范围。
在这里插入图片描述
        将要生成的数据放在相应块中,在各块中再进行遍历查找。这种查找方式作为分块查找的改进,被重新命名为哈希查找
在这里插入图片描述

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

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

相关文章

计算机Java项目|基于SpringBoot的精简博客系统的设计与实现

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参…

第47课 Scratch入门篇:水果忍者

水果忍者 故事背景: 水果忍者是一款传统的非常好玩的游戏,我们通过鼠标控制水果刀,把弹出的水果切掉,如果切到地雷则扣分,这款游戏非常好玩,现在我们现在通过Scratch 把它做出来,! 程序原理: 这款游戏难点就是水果的抛起和下降,由于角色是从下往上走,也就是 Y 坐标…

ip归属地换地方了会自动更新吗

在这个数字化时代&#xff0c;互联网已成为我们生活、工作和学习中不可或缺的一部分。而每一个连接互联网的设备&#xff0c;都会通过其IP地址与外界进行通信。IP地址&#xff0c;这个看似简单的数字组合&#xff0c;实则承载着设备位置、网络身份等重要信息。随着人们移动性的…

重生奇迹MU:穿越时光的辉煌篇章

在远古的曙光中&#xff0c;奇迹重现&#xff0c;MU大陆迎来了前所未有的荣耀重生&#xff01;重生奇迹MU&#xff0c;一款融合经典与创新&#xff0c;跨越时空界限的奇迹之旅&#xff0c;邀您共赴一场梦幻与热血交织的冒险盛宴。 经典重塑&#xff0c;传奇再续 重拾昔日辉煌…

贝叶斯神经网络bnn pyro 包 bayesian-neural-network在人工智能领域应用,预测金融市场价格

在人工智能领域&#xff0c;预测金融市场价格一直是一个热门话题。本文将介绍一种新颖的方法——贝叶斯神经网络&#xff0c;用于预测市场动态&#xff0c;以虚拟货币“以太币”为例进行说明。 ### 贝叶斯神经网络&#xff1a;一种新视角 对于希望深入了解贝叶斯神经网络的读者…

【递归专题一】Pow(x,n)-快速幂算法

Pow(x,n)-递归及迭代实现 题目链接&#xff1a;Pow(x,n) 解法一&#xff1a;暴力循环 如210则用10个2相乘&#xff0c;但是如果n231-1&#xff0c;难道我们还要用n231-1个2相乘吗&#xff1f;这样显然会超时。 解法二&#xff1a;快速幂算法 递归实现 任意数的指数都可以用二…

骑行耳机怎么选?精选五款权威实测热卖机型

作为一位深耕于运动科技领域多年的博主&#xff0c;可以说在此之间测试评论过各类运动装备&#xff0c;其中对于骑行爱好者而言&#xff0c;不可或缺的就是一款可以提高骑行体验的蓝牙耳机&#xff0c;其中&#xff0c;骨传导耳机凭借佩戴舒适健康等特点&#xff0c;收获了各类…

[星瞳科技]OpenMV如何进行串口通信?

串口通信上 视频教程27 - 串口通信发送数据&#xff1a;OpenMV串口发送数据 | 星瞳科技 视频教程28 - 串口通信接收数据&#xff1a;OpenMV串口接收数据 | 星瞳科技 介绍 为什么要用串口呢&#xff1f;因为要时候需要把信息传给其他MCU&#xff0c;串口简单&#xff0c;通用…

Qt-enable介绍使用(10)

目录 enable() 描述 相关 使用 观察切换过程 enable() 描述 相关 使用 观察切换过程 我们新建一个项目&#xff0c;设置两个按钮&#xff0c;并且把第二个按钮的objectName改一个明显的名字 关于名字这一点补充&#xff0c;显然我们在实际中要注意对象名字要尽量的通俗易…

C语言中函数sizeof和strlen区别

sizeof和strlen是C语言中的两个常用函数&#xff0c;它们的作用和使用方式有所不同。 sizeof sizeof是一个运算符而非函数&#xff0c;用于计算数据类型或变量占用的字节数。它可以计算任意数据类型&#xff08;包括基本类型、自定义结构体、数组等&#xff09;的大小。例如&…

如何使用博达网站群管理平台的树状导航

1 介绍 由于网站建设需要&#xff0c;需在首页的左边竖栏部分使用树状导航。我又过了一遍《网站群管理平台用户手册》&#xff0c;没发现如何在网站的首页设置树状导航组件。昨天&#xff0c;我之所以在创建树状导航上不知所措&#xff0c;是因为平台本身有一些误导&#xff0…

捷途山海T2 VS 唐DM-i:谁主沉浮?

自从新能源技术涌入汽车界&#xff0c;国内自主品牌便凭借电力驱动实现了跨越式发展。众多卓越车型如雨后春笋般涌现&#xff0c;在消费者市场中与合资品牌形成了激烈竞争&#xff0c;这无疑也增加了消费者选择车辆的难度。面对琳琅满目的优秀车型&#xff0c;消费者很容易陷入…

页面设计任务 个人简介页面

目录 任务要求 任务讲解 源码: 详细讲解 html部分 CSS部分 任务要求 页面结构: 创建一个基本的 HTML 页面&#xff0c;页面标题为“我的个人简介”。页面内容分为以下四个部分&#xff1a; 顶部导航栏: 包含至少三个导航链接&#xff0c;例如&#xff1a;“主页”、“关于…

计算机毕业设计选题推荐-二手房价分析与预测-Python爬虫可视化-算法

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

YOLOv8添加SE注意力机制有效提升检测精度(已跑通)

SE注意力机制概念 SSqueeze-and-Excitation (SE) 注意力机制是一种专注于增强网络模型对不同特征通道的重要性理解的机制。它通过对通道维度上的特征进行动态调整&#xff0c;增强了网络对重要特征的敏感性。 源码 import numpy as np import torch from torch import nn fro…

Ubuntu 22.04 上更换 Node 版本管理器(nvm)的源

在 Ubuntu 22.04 上更换 Node 版本管理器&#xff08;nvm&#xff09;的源&#xff0c;可以通过修改 nvm 的配置文件来实现。以下是更换 nvm 源的步骤&#xff1a; 打开终端。 备份原始配置文件&#xff08;可选&#xff0c;但推荐&#xff09;&#xff1a; mv ~/.nvm/nvm.sh …

计算病理学中的Vision Transformer应用与挑战综述|顶刊精析·24-08-21

小罗碎碎念 这篇论文题为《Vision Transformers for Computational Histopathology》&#xff0c;综述了视觉变换器&#xff08;Vision Transformers, ViTs&#xff09;在计算病理学中的应用&#xff0c;包括图像分类、分割和生存风险回归等任务&#xff0c;并探讨了面临的挑战…

Modbus-TCP——Libmodbus安装和使用(Ubuntu22.04)

1、简介 Modbus是一种通信协议&#xff0c;广泛用于工业自动化和过程控制领域&#xff0c;允许不同设备之间进行数据交换。libmodbus是一个用于 Modbus 协议的开源库&#xff0c;主要用于开发和实现 Modbus 协议的客户端和服务器应用程序。libmodbus 以 C 语言编写&#xff0c…

后端开发刷题 | 合并两个排序的链表

描述 输入两个递增的链表&#xff0c;单个链表的长度为n&#xff0c;合并这两个链表并使新链表中的节点仍然是递增排序的。 数据范围&#xff1a; 0≤n≤1000&#xff0c;−1000≤节点值≤1000 如输入{1,3,5},{2,4,6}时&#xff0c;合并后的链表为{1,2,3,4,5,6}&#xff0c;…

python入门基础知识! 新手必备,看完技术突飞猛进!

基本的类 python最基础、最常用的类主要有int整形&#xff0c;float浮点型&#xff0c;str字符串&#xff0c;list列表&#xff0c;dict字典&#xff0c;set集合&#xff0c;tuple元组等等。int整形、float浮点型一般用于给变量赋值&#xff0c;tuple元组属于不可变对象&#x…