查找算法

1.二分查找

二分查找必须对有序数组进行,每次先找到中间值进行比较,若大于当前值则向后继续按此方法找,若小于当前值则向前继续按此方法找。

二分查找的思路:

1.首先确定数组中间值:

mid = (left + right) / 2;

2.然后比较中间值mid和要查找的值num大小;

2.1若num>arr[mid],则向右递归;

2.2.若num<arr[mid],则向左递归;

2.3.若num=arr[mid],则返回值;

3.当left>right时结束递归;

package com.sratct.search;

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

/**
 * 二分查找
 */
public class BinarySearch {
    public static void main(String[] args) {
       int[] arr = {1, 5, 20, 60,60, 81};
         System.out.println(binarySearch(arr,0, arr.length-1, 61));
        System.out.println(binarySearch1(arr,0, arr.length-1, 61));
    }

    /**
     * 在一个有序数组中查找到一个数并返回下标
     * @param arr
     * @param left
     * @param right
     * @param num
     * @return
     */
    public static int binarySearch(int[] arr, int left, int right, int num) {
        int mid = (left + right) / 2;
        if (left <= right) {

            if (num < arr[mid]) { // 需要查找的值小于中间值
                // 左边递归
                return binarySearch(arr, left, mid-1, num);
            } else if (num > arr[mid]) {  // 需要查找的值大于中间值
                // 右边递归
                return binarySearch(arr,mid+1, right, num);
            } else {
                return mid;
            }
        }
        return -1;
    }

    /**
     * 在一个有序数组中查找一个数并返回所有相同的数的下标
     * @param arr
     * @param left
     * @param right
     * @param num
     * @return
     */
    public static ArrayList<Integer> binarySearch1(int[] arr, int left, int right, int num) {
        int mid = (left+right)/2;
        if (left <= right) {
            if (num < arr[mid]) { // 需要查找的值小于中间值
                // 左边递归
                return binarySearch1(arr, left, mid-1, num);
            } else if (num > arr[mid]) {  // 需要查找的值大于中间值
                // 右边递归
                return binarySearch1(arr,mid+1, right, num);
            } else {
                // 当找到值以后,向左向右查找到相同的值,下标存入集合

                List<Integer> integers = new ArrayList<>();
                // 向左边查找
                int temp = mid - 1;
                while (true) {
                    if (temp < 0 || arr[temp] != num) {
                        break;
                    }
                        integers.add(temp);
                        temp -=1;  //temp 左移
                }

                // 中间值
                integers.add(mid);

                // 右边
                temp = mid + 1;
                while (true) {
                    if (temp > arr.length-1 || arr[temp] != num) {
                        break;
                    }
                    integers.add(temp);
                    temp +=1;  //temp右移
                }
                return (ArrayList<Integer>) integers;
            }
        }
       return null;
    }
}

2.插值查找

1)插值查找类似于二分查找,不同的是插值查找每次自适应mid处开始查找;

2)左边所有left,右边所有right,key为要查找的值num;

二分查找和插值查找公式:

mid = (left + high) / 2 = left + 1/2(right-left); // 二分查找

mid = left + ((num - arr[left]) / (arr[right] - arr[left]) )* (right - left); //插值查找

package com.sratct.search;

/**
 * 插值查找
 */
public class InsertValueSearch {
    public static void main(String[] args) {
        int[] arr = new int[100];
        for (int i=0; i<arr.length; i++) {
            arr[i] = i+1;
        }
        System.out.println(insertValueSearch(arr,0,99,0));
    }

    // 插值查找
    public static int insertValueSearch(int[] arr, int left, int right, int num) {

        // 防止数组越界:num > arr[0] || num > arr[arr.length-1]
        if (left <= right || num > arr[0] || num > arr[arr.length-1]) {
            int mid = left + ((num - arr[left]) / (arr[right] - arr[left])) * (right - left);
            if (num > arr[mid]) {
                return insertValueSearch(arr, mid + 1, right, num);
            } else if (num < arr[mid]) {
                return insertValueSearch(arr, left, mid - 1, num);
            } else {
                return mid;
            }
        }
        return -1;
    }
}

3.斐波那契(黄金分割法)查找算法

1)黄金分割点指把一条线段分割为两部分,使其中一部分与全长之比等于另一部分与这部分之比。取前三位近似数值为0.618。

2)斐波那契数列{1,1,2,3,5,8,13,21,34,55}发现两个相邻的数之比近似为0.618。

3)工作原理:

斐波那契查找原理与前面两种相似,仅仅改变中间结点(mid)位置,此时mid为黄金分割点附近,

即mid = left + F(k-1)-1 (F为斐波那契数列),如图

对F(k-1)-1的理解:

1)有斐波那契数列F(k) = F(k-1) + F(k-2)的性质,可以得到(F(k)-1) = (F(k-1)-1) + (F(k-2)-1) +1,由此可得,只要顺序表的长度为F(k)-1,则可以将表分为长度为F(k-1)-1和F(k-2)-1两端,即如上图所示,从而中间位置为

mid = left + F(k-1) -1

  1. 类似的每一段都可以用相同方式分割;

3)但是顺序表的长度不一定为F(k)-1,所以需要将原来的顺序表长度n增加至F(k)-1,这里的k值只要能使得F(k)-1恰好大于等于n即可,有下面代码得到,顺序表长度增加后,新增的位置(从n+1到F(k)-1位置),都赋为n位置的值即可
while(n>flib(k)-1)

k++;

posted @ 2021-04-29 16:11  撑起一片阳光  阅读(78)  评论(0编辑  收藏  举报