zoukankan      html  css  js  c++  java
  • 数据结构之查找算法

    一、基本概念

    • 查找表:由相同类型的元素构成的集合
    • 静态查找表:进行查找时,没有频繁的删除和插入操作
    • 动态查找表:查找时,不仅存在查找操作,还有元素的插入与删除操作
    • 查找:给定一个确定的值,在查找表中寻找与给定值相同的数据元素

    二、分类

    • 线性查找算法
    • 核心思想:从头(尾)依次与查找表中的元素进行比较
    • 注意:查找表可以是有序表也可以是无序表
    • 复杂度分析
    • 最好时间复杂度:O(1)
    • 最坏时间复杂度:O(n)
    • 平均时间复杂度:O(n)
    • 优缺点:不适合数据量大的查找;适合数据量小的查找
    • 代码实现
      /查找唯一的一个值
      public static int searchXian(int[] arr,int value){
      for (int i = 0; i < arr.length; i++) {
      if (arr[i] == value){
      return i;
      }
      }
      return -1;
      }
      //优化,利用哨兵思想,上述每次都要判断i是否越界,这步操作可以省去
      //查找多个相同的值
      public static ArrayList<Integer> searchXian11(int[] arr,int value){
      ArrayList<Integer> arrayList = new ArrayList<>();
      for (int i = 0; i < arr.length; i++) {
      if (arr[i] == value){
      arrayList.add(i);
      }
      }
      return arrayList;
      }
    • 有序表查找
    • 二分查找算法
    • 核心思想:类似于分治思想。每次通过跟区间的中间元素进行比较,将待查找的区间缩小为之前的一半,直到找到要查找的元素
    • 注意:针对的是有序表;若是无序表,需要进行排序,然后利用二分查找。
    • 复杂度分析
    • 最好时间复杂度:O(1)
    • 最坏时间复杂度:O(n)
    • 平均时间复杂度:O(log n)
    • 场景局限性:
    1. 二分查找依赖的是顺序表,即数组。链表这种链式表不适合,因数组支持下标随机访问,时间复杂度为O(1),而链表为O(n)
    2. 二分查找针对的是有序表,同时不存在频繁的数据插入与删除的静态数据。此时只需要一次排序,多次二分查找,排序的成本可被均摊,二分查找的边际成本会比较低
    3. 数据量太小不适合二分查找。数据量小的情况下,二分查找与顺序查找没有太大的区别。特例:数组中存储的都是长度超过300的字符串,此时进行比较操作会比较耗时,推荐使用二分查找
    4. 数据量太大也不适合二分查找。这个主要从内存的连续来分析,因为二分查找的底层是数组,而数组对内存空间的要求是连续的存储空间,所以即使剩余的存储空间很大,但是不连续,也无法利用二分查找。
    5. 但凡能用二分查找的,基本上都倾向于用散列表或者二叉查找树。二分查找更适合“近似”查找问题
    • 二分查找的变形体(默认元素从小到大),针对的是有序数组中存在重复的元素
    • 查找第一个值等于给定值的元素
    • 查找最后一个值等于给定值的元素
    • 查找第一个大于等于给定值的元素
    • 查找最后一个小于等于给定值的元素
    • 代码实现
     1 //利用递归的方法,数组中不存在重复的元素
     2 public static int binarySearch(int[] arr, int low, int high, int value){
     3 //首先low > high ,说明没有找到
     4 if(low > high){
     5 return -1;
     6 }
     7 int mid = (low + high)/2;
     8 if(arr[mid] == value){
     9    return mid;
    10 }else if(arr[mid] > value){
    11    return binarySearch(arr, low, mid - 1, value);
    12 }else{
    13    return binarySearch(arr, mid + 1, high, value);
    14 }
    15 }
    //此时为有序数组,且存在重复的数值,并返回所有的下标。找到mid时,此时不返回,沿mid向两边扫描,将所有的下标值添加到ArrayList集合中
    public static ArrayList<Integer> binarySearch1(int[] arr,int low,int high,int findVal){
    //定义一个数组集合,用于存放下标
    ArrayList<Integer> arrayList = new ArrayList<>();
    //当low>high时,说明递归完没有找到值
    if (low > high){
    return new ArrayList<>();
    }
    int mid = (low + high)/2;
    if (findVal > arr[mid]){
    return binarySearch1(arr,mid + 1,high,findVal);//向右递归
    }else if(findVal < arr[mid]){
    return binarySearch1(arr,low,mid - 1,findVal);//向左递归
    }else{
    int tempLeft = mid - 1;
    int tempRight = mid + 1;
    while(arr[tempLeft] == arr[mid] && tempLeft >= 0){
    arrayList.add(tempLeft);
    tempLeft -= 1;
    }
    arrayList.add(mid);
    while (arr[tempRight] == arr[mid] && tempRight <= arr.length - 1){
    arrayList.add(tempRight);
    tempRight += 1;
    }
    return arrayList;
    }
    }
    //非递归方法,数组中不存在重复的元素
    public static int binarySearch1(int[] arr,int value){
    int low = 0;
    int high = arr.length - 1;
    while (low <= high){
         int mid = (low + high)/2;
            if (arr[mid] == value){
    return mid;
    }else if(arr[mid] > value){
    high = mid - 1;
    }else{
    low = mid + 1;
    }
    }
    return -1;
    }
    //编写二分查找代码的注意事项:
    1.注意结束条件。low <= high
    2.注意mid 的取值。(high + low)/2,当high和low 很大时,两者之和容易溢出。可以写成:low + (high - low)/2,若优化到极致的话,可以写为:low + ((high - low)>>1).原因:相比除法运算,计算机的位处理要快很多
    3.high和low值的更新
    4.O(1)常量级复杂度可能比O(log n)更高。例如:O(300000)和O(log n)相比,后者的执行效率会更高。

    //
    查找第一个值等于给定值的元素
    public static int binarySearch3(int[] arr, int value){
    int low = 0;
    int high = arr.length - 1;
    if(low > high){
      return -1;
    }
    while(low <= high){
    int mid = low + ((high - low)>>1);
      if(arr[mid] = value){
        if((arr[mid - 1] != value) || (mid == 0)){
          return mid;
        }else{
          high = mid - 1;
        }
      }elseif(arr[mid] > value){
          high = mid - 1;
      }else{
       low = mid + 1;
      }
    }
    }
    //查找最后一个值等于给定值的元素
    public static int binarySearch3(int[] arr, int value){
    int low = 0;
    int high = arr.length - 1;
    if(low > high){
      return -1;
    }
    while(low <= high){
    int mid = low + ((high - low) >> 1);
      if(arr[mid] = value){
        if((arr[mid + 1] != value) || (mid == arr.length - 1)){
          return mid;
        }else{
          low = mid + 1;
        }
      }elseif(arr[mid] > value){
          high = mid - 1;
      }else{
       low = mid + 1;
      }
    }
    }
    //查找第一个大于等于给定值的元素
    public static int binarySearch3(int[] arr, int value){
    int low = 0;
    int high = arr.length - 1;

    if(low > high){
      return -1;
    }
    while(low <= high){
    int mid = low + ((high - low)>>1);

      if(arr[mid] >= value){
        if((arr[mid - 1] != value) || (mid == 0)){
          return mid;
        }else{
          high = mid - 1;
        }
      }else{
       low = mid + 1;
      }
    }
    }
    //查找最后一个小于等于给定值的元素
    public static int binarySearch3(int[] arr, int value){
    int low = 0;
    int high = arr.length - 1;

    if(low > high){
      return -1;
    }
    while(low <= high){
    int mid = low + ((high - low)>>1);

      if(arr[mid] <= value){
        if((arr[mid + 1] != value) || (mid == arr.length - 1)){
          return mid;
        }else{
          low = mid + 1;
        }
      }else{
       high = mid - 1;
      }
    }
    }
    •  插值查找算法
    • 缘由:二分查找是以有序数组的区间的中间作为划分标准。那是否可以不以中间作为划分标准,而按照1/4或者1/8来划分呢?
    • 定义:将给定的值与查找表中的最大最小关键字比较后再进行查找。
    • 核心思想:mid = low + (value - arr[low])/(arr[high]-arr[low])
    • 时间复杂度:O(log n)
    • 适用场景:查找表比较长,数据分布比较均匀,插值比二分会好很多;但是如果存在极端的查找表,不建议使用插值查找。(来源于大话数据结构)
    • 斐波那契查找算法

     

    • 按照黄金分割的标准来划分有序数组
    • 时间复杂度:O(log n)
    • 优点:相比于二分查找,它只涉及加减操作,没有乘除操作

     

  • 相关阅读:
    Jmeter压力测试-结果分析(三十)
    C#正则密码验证
    Docker
    c# framework 缓存依赖
    NETMQ订阅,超时断线重连
    RabbitMQ发布/订阅模式
    RAS非对称加密解密,公钥私钥
    .NETCore批量插入数据BulkLoader
    .NETcore使用CSRedisCore操作Redis
    Rabbitmq简单队列
  • 原文地址:https://www.cnblogs.com/zyj-0917/p/12770884.html
Copyright © 2011-2022 走看看