Javaデータ構造とアルゴリズム-線形検索と二分探索と補間検索

1.線形探索

シーケンスがあります:{1,8、10、89、1000、1234}、シーケンスにこの名前が含まれているかどうかを判断します[注文検索]要件:見つかった場合は、検索を求めて添え字値を指定します。

package com.szh.search;

/**
 * 线性查找
 */
public class SeqSearch {

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

    public static void main(String[] args) {
        int[] arr = { 1, 9, 11, -1, 34, 89 }; //无序数组
        int index = seqSearch(arr, 34);
        if (index == -1) {
            System.out.println("没有找到这个值");
        } else {
            System.out.println("找到了这个值,对应下标为:" + index);
        }
    }
}


2.二分探索

2.1再帰的な実装

バイナリ検索:順序付けられた配列{1,8、10、89、1000、1234}でバイナリ検索を実行し、番号を入力してその番号が配列に存在するかどうかを確認し、存在しない場合は添え字を検索してください。 「いいえ」この番号。

授業後の思考の質問:{1,8、10、89、1000、1000、1234}順序付けられた配列に複数の同一の値がある場合、ここで1000などのすべての値を見つける方法。

package com.szh.search;

import java.util.ArrayList;
import java.util.List;

/**
 * 二分查找
 */
public class BinarySearch {

    /**
     * 二分查找算法
     * @param arr 数组
     * @param left 左边的索引
     * @param right 右边的索引
     * @param findVal 要查找的值
     * @return 找到返回对应的下标,没找到则返回-1
     */
    private static int binarySearch(int[] arr, int left, int right, int findVal) {
        //当 left > right 时,说明递归整个数组,但是没有找到,此时直接返回-1
        if (left > right) {
            return -1;
        }

        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 {
            return mid;
        }
    }

    /**
     * 有多个相同的数值时,如何将所有的数值都查找到,比如这里的 1000
     * 思路分析
     * 	 1. 在找到mid 索引值,不要马上返回
     * 	 2. 向mid 索引值的左边扫描,将所有满足 1000 的元素的下标,加入到集合ArrayList
     * 	 3. 向mid 索引值的右边扫描,将所有满足 1000 的元素的下标,加入到集合ArrayList
     * 	 4. 将Arraylist返回
     */
    private static List<Integer> binarySearch2(int[] arr, int left, int right, int findVal) {
        //当 left > right 时,说明递归整个数组,但是没有找到,此时直接返回空list
        if (left > right) {
            return new ArrayList<>();
        }

        int mid = (left + right) / 2;
        int midVal = arr[mid];
        if (findVal > midVal) {
            return binarySearch2(arr, mid + 1, right , findVal);
        } else if (findVal < midVal) {
            return binarySearch2(arr, left, mid - 1, findVal);
        } else {
            List<Integer> resIndexList = new ArrayList<>();
            //向 mid 索引值的左边扫描,将所有满足 1000 的元素的下标,加入到集合ArrayList
            int temp = mid - 1;
            while (true) {
                //索引小于0表示已到达数组最左边,arr[temp] != findVal表示找到不等于findVal的就可以退出了
                if (temp < 0 || arr[temp] != findVal) {
                    break;
                }
                resIndexList.add(temp); //找到了就加入list中
                temp--; //因为是向左查找,所以索引值依次-1
            }
            //别忘了,还要将最先查找到的mid下标加入list中
            resIndexList.add(mid);
            //向mid 索引值的右边扫描,将所有满足 1000 的元素的下标,加入到集合ArrayList
            temp = mid + 1;
            while (true) {
                //索引大于arr.length - 1表示已到达数组最右边,arr[temp] != findVal表示找到不等于findVal的就可以退出了
                if (temp > arr.length - 1 || arr[temp] != findVal) {
                    break;
                }
                resIndexList.add(temp); //找到了就加入list中
                temp++; //因为是向右查找,所以索引值依次+1
            }
            return resIndexList;
        }
    }

    public static void main(String[] args) {
        int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 , 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
		int resIndex = binarySearch(arr, 0, arr.length - 1, 14);
		System.out.println("resIndex = " + resIndex);

        System.out.println("---------------------------------");

        int[] array = { 1, 8, 10, 89, 1000, 1000, 1000, 3333, 9527};
        List<Integer> list = binarySearch2(array, 0, array.length - 1, 1000);
        System.out.println(list);
    }
}

二分探索アルゴリズムを使用してn個の要素の配列から数値を見つけると、最悪の場合、(log2 n)ステップかかるため、二分探索の時間計算量はですO(log2 n)

先ほど、再帰的手法を用いた二分探索アルゴリズムについてお話しましたが、次に二分探索アルゴリズムの非再帰的手法について説明します。

二分探索法は、順序付けられたシーケンス(数字や文字など)から検索し、シーケンスをソートしてから検索する場合にのみ適しています。二分探索法の実行時間は対数時間O(㏒₂n)です。つまり、[0,99](100個の数字、つまり、n = 100)目標数30を見つけるには、ステップ数を㏒₂100にする必要があります。つまり、最大7回(2 ^ 6 <100 <2 ^ 7)を見つける必要があります。

package com.szh.search;

/**
 * 二分查找(非递归实现)
 */
public class BinarySearch2 {

    /**
     * 二分查找的非递归实现
     * @param arr 待查找的数组, arr是升序排序
     * @param target 需要查找的数
     * @return 返回对应下标,-1表示没有找到
     */
    public static int binarySearch(int[] arr, int target) {
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (arr[mid] == target) {
                return mid;
            } else if (arr[mid] > target) {
                right = mid - 1; //需要向左边查找
            } else {
                left = mid + 1; //需要向右边查找
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        int[] arr = {1,3, 8, 10, 11, 67, 100};
        int index = binarySearch(arr, 100);
        System.out.println("index = " + index);
    }
}

 


3.補間ルックアップ

補間検索アルゴリズムは、補間検索が毎回アダプティブミッドから開始されることを除いて、バイナリ検索に似ています。

半分の検索で中間インデックスを見つけるための式。lowは左のインデックスを左に表し、highは右のインデックスを右に表します。重要なのは上記のコードのfindValです。

    →に変更 

int mid = low +(high-low)*(key --arr [low])/(arr [high] --arr [low]);/*插值目*/

前のコード式に対応:int mid = left +(right – left)*(findVal – arr [left])/(arr [right] – arr [left]) 

package com.szh.search;

/**
 * 插值查找
 */
public class InsertValueSearch {

    private static int insertValueSearch(int[] arr, int left, int right, int findVal) {
        int num = 0;
        System.out.println("插值查找的次数:" + (++num));

        //注意: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;
        }
    }

    public static void main(String[] args) {
        int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 , 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
        int resIndex = insertValueSearch(arr, 0, arr.length - 1, 14);
        System.out.println("查找元素的下标 resIndex = " + resIndex);
    }
}

補間ルックアップノート:

  1. 大量のデータと比較的均一なキーワードの分布を持つルックアップテーブルの場合、補間検索が使用されます。これはより高速です。
  2. キーワードの分布が不均一な場合、この方法は必ずしも半分にするよりも優れているとは限りません。

おすすめ

転載: blog.csdn.net/weixin_43823808/article/details/123531583