从零开始学Java-算法查找

100 阅读9分钟

正在学Java的各位,这几种Java编程中的经典算法千万不要错过,算法在Java开发中会起到非常重要作用,了解这些常用的算法会让你在工作中达到事半功倍的效果!

看看今天为大家整理经典算法详解:

目录

  • 基本查找
  • 二分查找
  • 分块查找

基本查找

基本查找又称顺序查找

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

我们一起来看一个案例:

需求:定义一个方法利用基本查找,查询某个元素是否存在。

数据如下:{131,127,147,81,103,23,7,79}

我们先来定一个方法用来判断是否存在

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

接着定义一个数组,调用方法查找某个值:

int[] arr = {131,127,147,81,103,23,7,79};
System.out.println(basicSearch(arr,81));
System.out.println(basicSearch(arr,82));

我们来运行看一下:

image.png

是不是81在就返回一个true,而82不在,直接给你返回一个false呀!

那我们想要返回对应的索引怎么办呢?其实也很简单:

// 查找对应的索引
public static int basicSearch1(int[] arr,int number){
    // 利用基本查找来查找number在数组中是否存在
    for (int i = 0; i < arr.length; i++) {
        if(arr[i] == number){
            return i;
        }
    }
    return -1;
}

image.png

怎么样,基本查找是不是比较简单呀!我们接着来学习下一个吧。

二分查找

二分查找也称折半查找

  • 前提条件:数组中的数据必须是有序的

  • 核心逻辑:每次排除一半的查找范围

image.png

我们来看一下这张图片,可以定义一个min表示0索引,max表示最大的索引,而二分查找就是将最大索引加上最小索引除2来求出mid范围。拿着中间的那个值和79比较,发现79在左边,那这个时候就可以把右边给忽略了: image.png

这个时候min索引是不变的,max索引所指的就发生变化了,接着还是拿最大索引加上最小索引除2来求出mid范围,这个时候就发现79是在右边,那这个时候max就不变了,把min往右边移:

image.png

这个时候在拿最大索引加上最小索引除2来求出mid范围,是不是就找到了79了呀!

  • 核心:
  1. min和max表示要查找的范围。
  2. mid是在min和max中间的。
  3. 如果要查找元素在mid的左边,缩小范围时,min不变,max等于mid减1.
  4. 如果要查找元素在mid的右边,缩小范围时,max不变,min等于mid加1.

下面我们也来实操一下吧:

需求:定义一个方法利用基本查找,查询某个元素是否存在。

数据如下:{7,23,79,81,103,127,131,147}

先来定义一个方法吧:

// 二分查找
public static int binarySearch(int[] arr,int number){
    // 1.定义两个变量记录要查找的范围
    int min = 0;
    int max = arr.length -1;
    // 2.利用循环不断去找要查找的数据
    while (true){
        if(min > max){
            return -1;
        }
        // 3.找到min和max的中间位置
        int mid = (min + max) / 2;
        // 4.拿着mid指向的元素跟要查找的元素进行比较
        if(arr[mid] > number){
            // 4.1 num在mid的左边 min不变,max = mid -1
            max = mid -1;
        }else if(arr[mid] < number){
            // 4.2 num在mid的右边 max不变,min = mid +1
            min = mid + 1;
        }else{
            // 4.3 num跟mid指向的元素一样 返回索引
            return mid;
        }
    }
}

接着定义一个数组,调用方法查找某个值:

int[] arr = {7,23,79,81,103,127,131,147};
System.out.println(binarySearch(arr, 127));
    System.out.println(binarySearch(arr, 150));

image.png

127找到了,直接返回了索引,而150不存在,所有直接返回一个-1。到这里二分查找就学习完毕啦。我们下面来做小结吧:

  • 小结:
  • 二分查找的优势:
    • 提高查找的效率
  • 二分查找的前提条件:
    • 数据必须是有序的
  • 二分查找的过程:
    • min和max表示要查找的范围
    • mid是在min和max中间的
    • 如果要查找元素在mid的左边,缩小范围时,min不变,max等于mid减1
    • 如果要查找元素在mid的右边,缩小范围时,max不变,min等于mid加1

分块查找

  • 分块原则1:前一块中的最大数据,小于后一块中所有的数据 (块内无序,块间有序)
  • 分块原则2:块数数量一般等于数字的个数开根号。比如16个数字一般分为4块左右
  • 核心思路:先确定要查找的元素在哪一块,然后在块内挨个查找

什么意思呢?我们来看一下:

image.png

是不是每一块的最小值大于前一块的数据呀。那怎么实现呢?我们来看一下分块查找的实现步骤:

  • 实现步骤:
    • 1.创建数组存放每一块对象信息
    • 2.先查找数组确定要查找的数据属于哪一块
    • 3.在单独遍历这一块数据即可

下面我们来看个案例吧: 现在有这么一个数组,完成分块:int[] arr = {16,5,9,12,21,18, 32,23,37,26,45,34,50,48,61,52,73,66};

我们是不是要对数组先进行分块呀:

// 1.要把数据进行分块
int[] arr = {16,5,9,12,21,18,
            32,23,37,26,45,34,
            50,48,61,52,73,66};

我们先来定义一个类:

//2.定义一个类
class Block{
    private int max; // 最大值
    private int startIndex; // 启始索引
    private int endIndex; // 结束索引
}

接着创建3个块的对象:

// 3.创建3个块的对象
Block b1 = new Block(21,0,5);   // 21最大数字 0表示开始索引 5表示结束索引
Block b2 = new Block(45,6,11);  // 45最大数字 6表示开始索引 11表示结束索引
Block b3 = new Block(73,12,17); // 73最大数字 12表示开始索引 17表示结束索引

定义索引表用来管理三个块的对象

Block[] blockArr = {b1,b2,b3};

接着定义一个变量表示要查找的元素

// 5.定义一个变量表示要查找的元素
int number = 23;
int number1 = 30;

利用分块查找的原理,查询number的索引

// 利用分块查找的原理,查询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.获取这一块的起始索引和结束索引
    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索引单独写一个方法,定义一个方法用来确定number在哪一块当中

// 定义一个方法用来确定number在哪一块当中
private static int findIndexBlock(Block[] blockArr, int number) {
    // 从0索引开始遍历blockArr,如果number小于max,那么就表示number是在这一块当中的
    for (int i = 0; i < blockArr.length; i++) {
        if (number <= blockArr[i].getMax()) {
            return i;
        }
    }
    return -1;
}

调用方法,传递索引表,数组,要查找的元素

// 6.调用方法,传递索引表,数组,要查找的元素
int index = getIndex(blockArr, arr, number);
int index1 = getIndex(blockArr, arr, number1);
System.out.println(number + "的索引为:" + index);
System.out.println(number1 + "的索引为:" + index1);

我们来运行看一下:

image.png

是不是也能获取到呀,如果没有元素的话就直接返回-1。分块查找这部分是比较难得,我们来看一下完整代码吧:

package Search;

public class Searchdemo3 {
    public static void main(String[] args) {
        // 1.要把数据进行分块
        int[] arr = {16, 5, 9, 12, 21, 18,
                32, 23, 37, 26, 45, 34,
                50, 48, 61, 52, 73, 66};
        // 3.创建3个块的对象
        Block b1 = new Block(21, 0, 5);   // 21最大数字 0表示开始索引 5表示结束索引
        Block b2 = new Block(45, 6, 11);  // 45最大数字 6表示开始索引 11表示结束索引
        Block b3 = new Block(73, 12, 17); // 73最大数字 12表示开始索引 17表示结束索引

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

        // 5.定义一个变量表示要查找的元素
        int number = 23;
        int number1 = 30;

        // 6.调用方法,传递索引表,数组,要查找的元素
        int index = getIndex(blockArr, arr, number);
        int index1 = getIndex(blockArr, arr, number1);
        System.out.println(number + "的索引为:" + index);
        System.out.println(number1 + "的索引为:" + index1);

    }

    // 利用分块查找的原理,查询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.获取这一块的起始索引和结束索引
        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在哪一块当中
    private static int findIndexBlock(Block[] blockArr, int number) {
        // 从0索引开始遍历blockArr,如果number小于max,那么就表示number是在这一块当中的
        for (int i = 0; i < blockArr.length; i++) {
            if (number <= blockArr[i].getMax()) {
                return i;
            }
        }
        return -1;
    }
}

//2.定义一个类
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 + "}";
    }
}

到这里基本的三种查找方式就学习完毕啦,我们下章节继续学习排序会更有意思哟,好啦有什么不懂的可以一起在评论区评论探讨,我们下期不见不散!!!

==最后非常感谢您的阅读,也希望能得到您的反馈  ==