数据结构与算法 11 查找算法 线性查找 二分查找 插值查找 斐波那契(黄金分割点)查找

查找算法

常用查找算法

  1. 顺序(线性)查找
  2. 二分查找/折半查找 (需要是有序数组)递归/非递归
  3. 插值查找
  4. 斐波那契查找/黄金分割点查找

线性查找

package search;

import java.util.ArrayList;

public class SequenceSearch {
    public static void main(String[] args) {
        int arr[] = {1,5,3,6,9,2,3,-1};
        ArrayList res = seqSearch(arr,3);
        System.out.println(res.toString());

    }
    // 这里找到一个满足条件的值就返回
    // 找所有满足条件的,就返回一个集合
    public static ArrayList<Integer> seqSearch(int[] arr, int value){
        // 逐一比对,发现相同的值,返回下标
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]==value){
                list.add(i);
            }
        }
        return list;
    }
}

二分查找

必须为有序序列

思路分析

  1. 首先确定该数组的中间的下标 mid = (left+right)/2

  2. 然后让需要查找的数 findVal 和 arr[mid] 比较

    • findVal > arr[mid],说明你要查找的数在 mid 的右边,因此需要递归的向右查找
    • findVal < arr[mid],说明你要找的数在 mid 的左边,因此需要递归的向左查找
    • findVal = arr[mid],返回
  3. 结束递归的条件

    • 找到就结束递归
    • 递归完整个数组,仍然没有找到 findVal,也需要结束递归 当 left > right 时,需要退出

package search;

public class BinarySearch {
    public static void main(String[] args) {
        int[] arr = {1,5,7,34,89,345,1234};
        int left = 0;
        int right = arr.length-1;
        int res = binary(arr,7,left,right);
        System.out.println(res);
    }
    public static int binary(int[] arr, int value,int left,int right){
        int mid = (left + right)/2;
        int midValue = arr[mid];
        if(left>right){
            return -1;
        }
        if(value > midValue){ // 向右递归
            return binary(arr,value,mid+1,right);
        }else if(value < midValue){
            return binary(arr,value,left,mid-1);
        }else{
            return mid;
        }
    }
}

当序列中有重复值时,都返回

  1. 在找到 mid 索引值,不要马上返回
  2. 向 mid 索引值的左边扫描,将所有满足要求的元素的下标加入到集合 ArrayList
  3. 向 mid 索引值的右边扫描,将所有满足要求的元素的下标加入到集合 ArrayList
  4. 将 ArrayList 返回

package search;

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

public class BinarySearch2 {
    public static void main(String[] args) {
        int[] arr = {1,5,7,34,89,89,89,345,1234};
        int left = 0;
        int right = arr.length-1;
        ArrayList res = binary(arr,89,left,right);
        System.out.println(res.toString());
    }
    public static ArrayList<Integer> binary(int[] arr, int value, int left, int right){
        int mid = (left + right)/2;
        int midValue = arr[mid];
        if(left>right){
            return new ArrayList<Integer>();
        }
        if(value > midValue){ // 向右递归
            return binary(arr,value,mid+1,right);
        }else if(value < midValue){
            return binary(arr,value,left,mid-1);
        }else{
            ArrayList<Integer> list =  new ArrayList<Integer>();
            int temp = mid - 1;
            while(true){ // 因为是有序序列,因此向两边找,如果相同,则返回
                if (temp<0 || arr[temp]!=value){
                    break;
                }
                list.add(temp);
                temp -= 1;
            }
            list.add(mid);

            temp = mid + 1;
            while(true){
                if(temp>arr.length || arr[temp]!=value){
                    break;
                }
                list.add(temp);
                temp += 1;
            }
            return list;
        }
    }
}

插值查找

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

  2. 将折半查找中的求 mid索引的公示,low 表示左边索引 left,high 表示右边索引 right,key 指的是要找的那个人

  3. int midIndex = low + (high - low)*(key - arr[low]) / (arr[high] - arr[low])


interpolation

package search;

import javax.sound.midi.MidiChannel;
import java.util.Arrays;

public class InsertSearch {
    public static void main(String[] args) {
//        int[] arr = new int[100];
//        for (int i = 0; i < 100; i++) {
//            arr[i] = i + 1;
//        }
        int[] arr = {1,3,6,67,456,2345};
        int index = insert(arr,0,arr.length-1,3);
        System.out.println(index);
    }

    // insert search
    public static int insert(int[] arr,int left,int right,int value){
        if(left>right || value < arr[0] || value > arr[arr.length-1]){
            return -1;
        }
        int midIndex = left + (right - left)*(value - arr[left]) / (arr[right] - arr[left]);
        int midValue = arr[midIndex];
        if(value>midValue){ // 向右边递归
            return insert(arr,midIndex+1,right,value);
        } else if(value<midValue){ // 向左递归
            return insert(arr,left,midIndex-1,value);
        } else{ // find it!
            return midIndex;
        }
    }
}

对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找,速度较快

关键字分布不均,折半查找较好


斐波那契查找算法

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

F(k) = F(k-1) + F(k-2) 可以得到 F[k] -1 = (F[k-1]-1) + (F[k-2]+1)

需要顺序表的长度为 F[k] -1,则可以将该表分为长度为 F[k-1]-1 和 F[k-2]+1两部分,中间位置为 mid = low + F(k-1) - 1

如果顺序表的长度n不是 F[k] -1,则需要将原来的长度n添加至 F[k] -1,都赋值为n


借助斐波那契数列找到黄金分割点,先把数组扩成能找到分割点的长度


package search;

import java.util.Arrays;

public class FibonacciSearch {
    public static int maxSize = 20;
    public static void main(String[] args) {
        int[] arr = {1,8,10,89,1000,1234};
        System.out.println(fibSearch(arr,100));
    }

    // get fibonacci
    public static int[] fib(){
        int[] f = new int[maxSize];
        f[0] = 1;
        f[1] = 1;
        for (int i = 2; i < maxSize; i++) {
            f[i] = f[i-1]+f[i-2];
        }
        return f;
    }

    // search
    // not recursion
    public static int fibSearch(int[] a,int key){
        int low = 0;
        int high = a.length-1;
        int k = 0; // the index of fib splitting data
        int mid = 0;
        int f[] = fib();
        // get k
        while(high>f[k]-1){
            k++;
        }
      
        // high = 5
        // f = 1,1,2,3,5,8,13,21,34,...
        // k = 5
        // f[k] = 8
        // 所以需要新建一个数组 copy原数组,且新数组的长度为8,
        // 超过的部分填充为原数组的最后一个数
      
        // f[k] may greater than a.length, so Arrays is used to build
        // a new array points to a[]
        int[] temp = Arrays.copyOf(a,f[k]);
        for (int i = high+1; i < temp.length; i++) {
            temp[i] = a[high];
        }
        while(low<=high){
            mid = low+f[k-1]-1;
            if(key<temp[mid]){ // search left of the array
                high = mid-1;
                k--;
                // 全部元素=前面的元素+后面的元素
                // F[K] = F[K-1]+F[K-2]
                // 因为前面有 F[K-1] 个元素,所以可以继续拆分 F[K-1]=F[K-2]+F[K-3]
                // 在F[K-1]的前面继续查找,所以为 k--
                // 即下次循环 mid = low+f[k-1-1]-1
            } else if (key>temp[mid]){ // search right
                low = mid + 1;
                k-=2;
                // why k-2 ?
                // 全部元素=前面的元素+后面的元素
                // F[K] = F[K-1]+F[K-2]
                // 因为后面有F[K-2]个元素,所以可以继续拆分 F[K-2]=F[K-3]+F[K-4]
                // 即在 F[K-2]的前面进行查找 k-=2
                // 下次循环 mid = f[k-1-2]-1
            }else{
                // find it
                if(mid<=high){
                    return mid;
                }else{
                    return high;
                }
            }
        }
        return -1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值