数据结构-查找

  • 查找的概念:根据数据存储的方式,查找操作可以分为两种主要类型:静态查找(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)
    优点实现简单,无需额外空间。查找效率高。适用于元素分布不均匀的数据查找查找效率高,适用于元素分布不均匀的数据查找。查找效率高,无需排序。
    缺点查找效率低。数据需已排序。实现相对复杂。实现相对复杂。散列函数设计困难,可能出现冲突。
    应用场景适用于数据量较小、查找频率较高的场景。适用于数据量大、查找频率较高的场景。适用于数据量较大、查找频率较高的场景。适用于数据量较大、查找频率较高的场景。适用于数据量较小、查找频率较高的场景。
  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Iron_Sp-man

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值