(详细)查找数据结构大集合(Java)

1.顺序查找

顺序查找,顾名思义,遍历整个数列,当数列值和该查找值相等就返回该查找所在数列的索引

1.1代码实现

package Search;

/**
 * @author zh
 * @ClassName : Search.OrderSearch
 * @Description :顺序查找
 * Created by user on 2021-07-21 16:57:09
 * Copyright  2020 user. All rights reserved.
 */
public class OrderSearch {
    static int[] arr;
    /**
     * 顺序查找
     * @author zh
     * @date 2021/7/21 16:57
     * @param arr:  查找元素的数组
     * @param val:  查找值
     * @return int :返回查找值在数组的索引
     **/
    public static int orderSearch(int[] arr,int val){
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == val){
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        arr=new int[]{3,2,0,1};
        System.out.println(orderSearch(arr, 0));
    }
}

2.二分查找

二分查找也称折半查找,原理非常简单,实质就是通过不断改变中间值mid的索引来缩小查找的范围
注:一个数列使用二分查找的前提是该数列是有序的(本文的一切排序都假定数列是从大到小排序的)

2.1二分查找思路及步骤

二分查找实现可以通过递归或非递归(循环)来实现,原理有一样,简单易懂

  1. 首先定义left左指针和right右指针分别用来指定,查找的数列的索引范围(left和right的初始值分别为0,arr.length-1)
  2. 通过数组的长度确定中间值mid的索引---->mid=(left+right)/2
  3. 然后将要查找的值val与arr[mid]进行比较,如果val==arr[mid],那么直接返回该索引;如果val<arr[mid],那么向左开始递归或循环地进行查找;如果val>arr[mid],那么向右开始递归或循环进行查找.
  4. 递归或循环地结束条件:当在递归中查找到目标值,直接结束,如果最终该值不在该数列中,那么结束的条件为left>right(因为随着递归,left和right之间差值越来越小,直至相等);当在循环时结束条件一样

2.2代码实现

以下二分查找实现了当查找值在数列有多个相同值时,返回该值所有的索引集合

package Search;

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

/**
 * @author zh
 * @ClassName : Search.BinarySearch
 * @Description :二分查找/折半查找(使用该查找的前提是该数列是有序的)
 * Created by user on 2021-07-21 18:43:52
 * Copyright  2020 user. All rights reserved.
 */
public class BinarySearch {
    static int[] arr;

用递归实现

 /**
     * 二分查找(当数列中有多个相同的查找值,可返回所有的索引)
     * @author zh
     * @date 2021/7/21 18:44
     * @param arr: 数组
     * @param val:  值
     * @param left:  左索引
     * @param right:  右索引
     * @return List<Integer> :索引值的集合
     **/
    public static List<Integer> binarySearch(int[] arr, int left, int right, int val){
        /*递归*/
        if (left>right){
            return new ArrayList<Integer>();
        }
       int  mid=(left+right)/2;
        if (val==arr[mid]){
            ArrayList<Integer> list = new ArrayList<>();
            int temp=mid;
            list.add(mid);
            while (mid-1!=-1&&arr[mid-1] == val){
                mid--;
                list.add(mid);
            }
            while (temp+1<arr.length-1&&arr[temp+1]==val){
                temp++;
                list.add(temp);
            }
            return list;
        }else if(val<arr[mid]){
            return binarySearch(arr,left,mid-1,val);
        }else {
            return binarySearch(arr,mid+1,right,val);
        }
    }

用循环实现

 /**
     * 二分查找(当数列中有多个相同的查找值,可返回所有的索引)
     * @author zh
     * @date 2021/7/21 18:44
     * @param arr: 数组
     * @param val:  值
     * @param left:  左索引
     * @param right:  右索引
     * @return List<Integer> :索引值的集合
     **/
    public static List<Integer> binarySearch(int[] arr, int left, int right, int val){
     //非递归
       int  mid;
       while (left<=right){
          mid = (left+right)/2;
          if (val==arr[mid]){
              ArrayList<Integer> list = new ArrayList<>();
              int temp=mid;
              list.add(mid);
              while (mid-1!=-1&&arr[mid-1] == val){
                  mid--;
                  list.add(mid);
              }
              while (temp+1<arr.length-1&&arr[temp+1]==val){
                  temp++;
                  list.add(temp);
              }
              return list;
          }else if (val<arr[mid]){
              right=mid-1;
          }else {
              left=mid+1;
          }
       }
       return new ArrayList<>();
    }

测试主方法

   public static void main(String[] args) {
        arr=new int[]{1,43,43,43,3242,5351,45111,4541111,5611111,6666666} ;
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
        System.out.println(binarySearch(arr, 0, arr.length - 1, 1));
    }

3.插值查找

3.1插值查找原理介绍

插值查找算法基于二分查找,和二分查找唯一的不同之处就是mid的索引取值不同,插值查找每次从自适应mid 处开始查找, mid=left+(right-left)*(val-arr[left])/(arr[right]-arr[left])),该种mid的算法是根据数学中的归一法(好像是).
简单补充:归一法是一种简化计算的方式,有两种形式,一种是把数变为(0,1)之间的小数,一种是把有量纲表达式变为无量纲表达式.
当查找的数列的数排列均匀(即每个数之间相差不多)的情况,那么插值查找查找的次数要比二分查找少;反之,次数更多.
插值查找注意事项:

  1. 对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找, 速度较快.
  2. 关键字分布不均匀的情况下,该方法不一定比折半查找要好

3.2代码实现

package Search;
/**
 * @author zh
 * @ClassName : Search.InsertSearch
 * @Description :插值查找(基于二分查找,主要为mid和二分查找不同,
 *               mid=left+(right-left)*(val-arr[left])/(arr[right]-arr[left]))
 * Created by user on 2021-07-21 20:14:24
 * Copyright  2020 user. All rights reserved.
 */
public class InsertSearch {
    static int[] arr;
    /**
     * 插值查找
     * @author zh
     * @date 2021/7/21 20:20
     * @param arr:
     * @param left:
     * @param right:
     * @param val:
     * @return int :
     **/
    public static int insertSort(int[] arr,int left,int right,int val){
        System.out.println("看查找次数");
        /*这里的arr[0]>val||arr[arr.length-1]<val一定要判断,因为当val超过最大值或小于最小值,
        那么mid就会很大或很小导致数组越界*/
        if (left>right||arr[0]>val||arr[arr.length-1]<val){
            return -1;
        }
        int mid=left+(right-left)*(val-arr[left])/(arr[right]-arr[left]);
        if (val==arr[mid]){
            return mid;
        }else if (val < arr[mid]) {
            return insertSort(arr,left,mid-1,val);
        }else {
            return insertSort(arr,mid+1,right,val);
        }
    }

    public static void main(String[] args) {
        arr=new int[]{1,43,43,43,3242,5351,45111,4541111,5611111,6666666};
        System.out.println(insertSort(arr, 0, arr.length - 1, 45111));
    }
}

4.斐波那契查找

4.1斐波那契查找的原理介绍

斐波那契查找也称黄金分割查找,他也是基于二分查找的算法,其核心还是通过改变mid的索引达到缩小查找范围,
按照该算法mid=low+f(k-1)-1
斐波那契查找原理:
首先斐波那契数列有一特性f(k)=f(k-1)+f(k-2),而随着k的增大f(k-1)/f(k)越接近于黄金分割值0.618…,所以该方法也称黄金分割查找;那么根据该算法,把查找数列分成了两个部分,前面部分为f(k-1)长度,后面部分为f(k-2)长度.而mid=low+f(k-1)-1,很容易看出mid的索引指向的值为f(k-1)中的最后一位数.下面是图片
在这里插入图片描述

4.1代码实现及思路

package Search;
import java.util.Arrays;

/**
 * @author zh
 * @ClassName : Search.FibonacciSearch
 * @Description :斐波那契查找(基于二分查找,只是mid变了mid=left+f(k-1)-1) (f(k-1)为斐波那契数)
 *               所以该查找算法的实质性是通过斐波那契数列找到mid的索引进行再查找
 * Created by user on 2021-07-22 09:06:49
 * Copyright  2020 user. All rights reserved.
 */
public class FibonacciSearch {
    /*该查找算法将数列长度记为f(k),由于f(k)为斐波那契数,所以f(k)=f(k-1)+f(k-2),
    所以将数列划分成前面一段为f(k-1),后面一段为f(k-2),mid=left+f(k-1)-1(即mid为f(k-1)中的最后一位数)*/

    static int[] arr;

    /*该长度是标明测试的数组的最大长度为6765,也就是斐波那契数列的第20位*/
    static int maxSize=20;

获得斐波那契数列

 /*获得斐波那契数列*/
    public static int[] fibonacci(){
        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;
    }

进行斐波那契查找

 /**
     * 斐波那契查找
     * @author zh
     * @date 2021/7/22 9:25
     * @param arr:
     * @param val :要查找的值
     * @return int :返回索引
     **/
    public static int fibonacciSearch(int[] arr,int val){
        /*当数组长度等于1时,由于right=0,得到k的值为0,
        所以temp数组长度也为1,导致计算mid时fibonacci(k-1)出现越界*/
        if (arr.length==1){
            if (arr[0]==val){
                return 0;
            }else {
                return -1;
            }
        }
        int left=0;
        int right=arr.length-1;
        int mid=0;
        /*斐波那契数列的索引*/
        int k=0;
        int[] fibonacci = fibonacci();
        /*根据数组的长度获得斐波那契数列中最接近且刚好大于或等于的数(即length<=fibonacci[k]-->得到k)
        * 比如数组长度是7,那么斐波那契数列中最接近的是8,那么k等于5*/
        while (right>fibonacci[k]){
            k++;
        }
        /*由于斐波那契数可能不等于数组长度,而是大于数组长度,
        所以需要扩容数组(构造一个新的数组,长度为fibonacci[k],并且将arr数组
        复制到该数组中并将大于该数组长度的部分的元素值赋值为arr数组中最大的数,
        即arr[arr.length-1],以此来保证新数组仍然是个有序数组)*/
        int[] temp = Arrays.copyOf(arr, fibonacci[k]);
        for (int i = arr.length-1; i < temp.length; i++) {
            temp[i]=arr[arr.length-1];
        }
        while (left<=right){
            /*temp[mid]是左半段最后一位,mid的和跟斐波那契数列中的左半段fibonacci[k-1]有关*/
            mid=left+fibonacci[k-1]-1;
            if (val>temp[mid]){
                left=mid+1;
                /*首先我们知道k的值是跟为了确定mid的位置,k-2的原因是,当val>mid,那么就去右半段fibonacci[k-2]中找
                * fibonacci[k-2]=fibonacci[k-3]+fibonacci[k-4],fibonacci[k-3]为左半段,那么mid肯定
                * 跟fibonacci[k-3]有关,而现在为fibonacci[k-1]-->fibonacci[k-1]需要k-2;
                * */
                k-=2;
            }else if(val<temp[mid]){
                right=mid-1;
                /*和上面的判断方法一样,val<mid就去左半段查找,并寻找左半段fibonacci[k-1]的mid,
                fibonacci[k-1]=fibonacci[k-2]+fibonacci[k-3],
                中间过程和上面一样,fibonacci[k-1]--->fibonacci[k-2]需要k-1;*/
                k--;
            }else {
                /*由于之前数组已经扩容,所以mid可能会大于原来的数组长度,所以索引可能会不准确
                * 但是当mid>arr.length-1时,代表查找的值为数组中最大的,所以直接返回原数组arr中的最后一个元素的索引*/
               if (mid<=arr.length-1){
                   return mid;
               }else {
                   return arr.length-1;
               }
            }
        }
        return -1;
    }

测试主方法

    public static void main(String[] args) {
        arr=new int[] {323,324,433,434,33253,432354,54234232};
        System.out.println(fibonacciSearch(arr, 323));
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值