数据结构-查找

  • 查找的概念:根据数据存储的方式,查找操作可以分为两种主要类型:静态查找(Static Search)和动态查找(Dynamic Search)。

    静态查找是指在数据集合中查找某个元素的位置或相关信息,数据集合在查找过程中保持不变。静态查找通常使用顺序查找、二分查找、插值查找等方法。

    动态查找是指在数据集合中查找某个元素的位置或相关信息,数据集合在查找过程中可能发生变化。动态查找通常使用二叉搜索树、B树、哈希表等数据结构进行查找。

  • 顺序查找(线性查找):顺序查找(Sequential Search)是一种简单的查找方法,它从列表的第一个元素开始,逐个比较元素与目标值的大小,直到找到目标值或遍历整个列表。如果找到目标值,则返回其索引;否则返回-1。

  • 折半查找(二分查找):二分查找是一种在有序数组中查找特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。

        public static  int binarySearch(int[] arr,int target){
            int left=0;
            int right=arr.length-1;
            while (left<right){
                int mid=left+(right-left)/2;
                if (arr[mid]==target){
                    return mid;
                }else if (arr[mid]<target){
                    left=mid+1;
                }else {
                    right=mid-1;
                }
            }
            return -1;
        }
    
  • 分块查找(索引顺序查找):分块查找(也称为索引查找)是一种在有序数组中进行查找的算法。它的基本思想是将一个大的有序数组分成多个小的块,对每个块进行排序,然后为每个块建立一个索引。在进行查找时,首先通过索引找到目标值可能所在的块,然后在对应的块中进行顺序查找。

        public static  int blockSearch(int[] arr,int target,int blockSize){
            //获得块数
            int blockNum=(int)Math.ceil((double) arr.length/blockSize);
            //创建二维数组,存储每个块在数组中的起始索引值
            int[][] index=new int[blockNum][2];
            //将每个块的对应索引存入二维数组中
            for (int i=0;i<blockNum;i++){
                int left=i*blockSize;
                int right=Math.min((i+1)*blockSize-1,arr.length-1);
                index[i][0]=left;
                index[i][1]=right;
            }
            //在块内寻找目标数据
            for (int[] block:index){
                if (target>arr[block[0]]&&target<arr[block[1]]){
                    for (int i=block[0];i<=block[1];i++){
                        if (arr[i]==target){
                            return  i;
                        }
                    }
                }
            }
            return -1;
        }
    
  • 树形查找

        public Value find(Key key) {
            Node node = root;
            while (node != null) {
                int cmp = key.compareTo(node.key);
                if (cmp < 0) {
                    node = node.left;
                } else if (cmp > 0) {
                    node = node.right;
                } else {
                    return node.value;
                }
            }
            return null;
        }
    
    • 各个查找算法的比较
      • 平衡二叉树查找是一种改进的二叉排序树,它的每个节点的两个子树的高度差不会超过1,从而保证了查找、插入和删除操作的时间复杂度为O(log n)。
      • 红黑树是在平衡二叉树的基础上进行改进的一种数据结构,它的主要目的是为了在保持树的高效率的同时,减少平衡二叉树因旋转操作带来的额外开销。
      • B+树是一种平衡的多路搜索树,它解决了磁盘I/O操作频繁的问题,提高了磁盘访问效率。在文件系统和数据库中,数据通常存储在磁盘上。磁盘访问速度相对较慢,因为磁盘旋转需要时间,并且磁头需要移动到正确的位置。因此,减少磁盘I/O操作是非常重要的,以提高系统的性能。
  • 散列查找

    • 散列表的概念:散列表(Hash Table)是一种数据结构,用于存储键值对(Key-Value Pair)或键值三元组(Key-Value-Triple)。它通过使用一个哈希函数(Hash Function)将键映射到数组中的一个位置,从而实现快速查找、插入和删除操作。
    • 散列函数的构造方法
      • 注意事项:

        1.散列函数的定义域必须包含全部需要存储的关键字,而值域的范围则依赖于散列表的大小或者地址大小

2.散列函数计算出来的地址尽可能的等概率,均匀分散在整个地址空间中,减少冲突的发生
3.散列函数应该尽可能简单,能够在较短时间内计算出任意关键字的散列地址
- 常见的散列函数构造方法

  1.直接定址法:直接将输入值作为输出值。例如,将一个整数作为键,那么散列函数可以定义为:H(x) = x。适用情况:当输入值的范围不大,且与散列地址有直接关系时适用

2.除留取余法:假定散列表长度为m,取一个不大于m但接近m的质数作为种子,进行散列函数H(key)=key%p运算得出散列地址.
3.数字分析法:使用一个随机数作为散列函数的种子,将输入值与种子进行某种运算得到输出值。例如,可以定义散列函数为:H(x) = (ax + b) mod p,其中a、b和p是随机数,x是输入值。适用情况:当需要对密码进行周期性和可预测性分析时适用。
4.平方取中法:取关键字平方值的中间几位作为散列地址.:当输入值的范围较大,且需要较小的散列地址范围时适用

  • 解决冲突的方法
    • 开放定址法,数学递推公式H=(H(key)+di)%m,其中H(key)为散列函数,m表示散列表表长,di为增长序列
      • 线性探测法:线性探测法是一种解决散列表冲突的方法,当散列函数产生的散列地址已经被占据时,线性探测法会从散列地址开始,依次检查其后面的地址,直到找到一个空地址。
      • 平方探测法:平方探测法是一种解决散列表冲突的方法,当散列函数产生的散列地址已经被占据时,平方探测法会从散列地址开始,依次检查其后面的地址,直到找到一个空地址。与线性探测法不同的是,平方探测法的地址间隔会随着探测次数的增加而增加.
      • 双散列法:双散列法(Double Hashing)是一种解决散列表冲突的方法,它使用两个散列函数来计算散列地址。当散列函数产生的散列地址已经被占据时,双散列法会使用第二个散列函数计算一个新的散列地址,直到找到一个空地址。
      • 伪随机序列法:伪随机序列法是一种基于随机数的散列函数构造方法。它通过一个伪随机数生成器来生成一系列伪随机数,并将这些伪随机数作为散列函数的输出。伪随机序列法通常用于生成加密散列函数,以提高散列函数的安全性。
    • 拉链法:拉链法是一种解决散列表冲突的方法,它将具有相同散列值的键值对存储在同一个链表中。拉链法允许散列表中的元素随机分布,从而减少冲突的可能性。
  • 散列函数的性能分析
    • 装填因子:装填因子=表中记录数/散列表长度.
    • 查找成功的平均查找长度:每个关键字查找成功的比较次数之和/关键字数量
    • 查找不成功的平均查找长度:关键字的个数/散列表的长
  • 查找算法的比较
    顺序查找 折半查找 分块查找 树形查找 散列查找
    适用情况 适用于元素分布均匀、无特定规律的数据查找 适用于已排序的数据查找。 适用于元素分布不均匀、有特定规律的数据查找。 适用于元素分布不均匀、有特定规律的数据查找。 适用于元素分布均匀、无特定规律的数据查找。
    时间复杂度 O(n) O(log n) O(log n) O(log n) O(1)
    空间复杂度 O(1) O(1) O(1) O(log n) O(n)
    优点 实现简单,无需额外空间。 查找效率高。 适用于元素分布不均匀的数据查找 查找效率高,适用于元素分布不均匀的数据查找。 查找效率高,无需排序。
    缺点 查找效率低。 数据需已排序。 实现相对复杂。 实现相对复杂。 散列函数设计困难,可能出现冲突。
    应用场景 适用于数据量较小、查找频率较高的场景。 适用于数据量大、查找频率较高的场景。 适用于数据量较大、查找频率较高的场景。 适用于数据量较大、查找频率较高的场景。 适用于数据量较小、查找频率较高的场景。

相关推荐

  1. 数据结构-二分查找

    2024-04-25 15:22:06       22 阅读
  2. 数据结构】分块查找

    2024-04-25 15:22:06       19 阅读
  3. 数据结构-查找

    2024-04-25 15:22:06       12 阅读
  4. 数据结构---查找

    2024-04-25 15:22:06       12 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-25 15:22:06       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-25 15:22:06       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-25 15:22:06       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-25 15:22:06       20 阅读

热门阅读

  1. PostCSS概述以及详细使用代码案例

    2024-04-25 15:22:06       12 阅读
  2. postcss简介

    2024-04-25 15:22:06       12 阅读
  3. 排列类枚举(递归)

    2024-04-25 15:22:06       13 阅读
  4. C++11 数据结构6 栈的链式存储,实现,测试

    2024-04-25 15:22:06       16 阅读
  5. 脚本:监控Oracle中正在运行的SQL

    2024-04-25 15:22:06       13 阅读
  6. 【Leetcode】33- 搜索旋转排序数组

    2024-04-25 15:22:06       12 阅读
  7. Leetcode30-最小展台数量(66)

    2024-04-25 15:22:06       15 阅读
  8. (五)Servlet教程——Servlet是什么

    2024-04-25 15:22:06       12 阅读
  9. 1002 - 编程求解1+2+3+...+n

    2024-04-25 15:22:06       16 阅读
  10. Gradle的安装配置及使用

    2024-04-25 15:22:06       16 阅读
  11. nvm安装

    2024-04-25 15:22:06       16 阅读
  12. 服务端测试与功能测试

    2024-04-25 15:22:06       15 阅读
  13. 买卖股票+跳跃游戏 贪心算法

    2024-04-25 15:22:06       12 阅读