三大基础查找(顺序、二分、分块)

目录

前言

一、查找的基本概念

1.静态和动态查找

2.内查找和外查找

3.平均查找长度ASL

二、三大基础查找

1.顺序查找

2.折半(二分)查找

3.分块(索引)查找


前言

  查找和排序是数据结构与算法中不可或缺的一环,是前辈们在算法道路上留下的重要且方便的一些技巧,学习这些经典的查找和排序也能让我们更好和更快的解决问题。在这个专栏中我们会学习六大查找和十大排序,而本篇将详细讲解其中的三大基础查找——顺序查找、折半(二分)查找、分块(索引)查找


一、查找的基本概念

这些概念非常的简单,我们只需要快速的过一遍即可;

1.静态和动态查找

  • 静态查找:数据集合相对来说比较稳定,不需要进行插入和删除;
  • 动态查找:在查找的过程中同时需要插入和删除的操作;

2.内查找和外查找

  • 在内存中进行的是内查找;
  • 在外存(硬盘)中进行的是外查找;

3.平均查找长度ASL

  • 也就是评判查找的效率的要素,越小越好,其中N是查寻元素的个数,P是查到概率,Pi=1/n,通常假设每个元素查找概率相同,C是找到第 i 个元素的次数;

ASL=\sum_{i=1}^{N}Pi * Ci

二、三大基础查找

1.顺序查找

顺序查找那是非常的简单,就是用 for循环遍历数据去判断就行了; 虽然顺序查找简单,但它是唯一 一 个可以针对于无序的数据进行查找;

即使是顺序查找也是有优化的方法的,这里还可以提一句,我们针对一个查找算法进行优化就是减小在上文中提到的平均查找长度ASL;(在真实项目中)那么对于顺序查找而言我们只需要在插入数据的时候把相对被查找概率大的数据放在前面;

2.折半(二分)查找

对于经常刷算法题的同学,折半(二分)查找一定很熟悉;当然不熟悉也不要紧,二分查找的思路也很简单:

首先,二分查找运用的前提条件是数据要有序(不一定时整体,也可以是在某一个区间内有序)它要做的就是:每一次都要确定查找的区间并减小区间范围

有如下一个数组:left是区间的左指针,right是区间的右指针,中间指针middle = (left + right) / 2;每一次查找都要用 mid 和待查值 key 做比较以此收缩要查找的区间,达到 “二分” 的效果;

①起初 left = 0,right = 4 那么 mid = 2,指向元素3,发现比待查值4要小于是待查区间应该在mid的右边,所以 left = mid + 1 而 right 不动,重新计算 mid = 3 指向元素4,是要找的元素,查找结束;

 二分查找的代码实现也很简单,只需要按照上面的逻辑实现就好了;

#include<stdio.h>
#include<stdlib.h>

//折半(二分)查找
int binarySearch(int a[], int n, int key)
{
    int low = 0;
    int high = n - 1; 
    while (low <= high)
    {
        int mid = (low + high) / 2;
        int midValue = a[mid];
        if(midValue < key)
        {
            low = mid + 1;
        }
        else if(midValue > key)
        {
            high = mid - 1;
        }
        else
        {
            return mid;
        }
    }
    return -1;
}

int main()
{
    int arr[] = {1, 2, 3, 4, 5};
    int length = sizeof(arr) / sizeof(arr[0]);
    int pos = binarySearch(arr, length, 4);
    printf("%d", pos);
    system("pause");
    return 0;
}

 运行结果

优点:减少查找的次数;

缺点:遇到的待查值是最大、最小值时较慢;

3.分块(索引)查找

分块查找也叫做索引顺序查找,所以在介绍分块查找之前我们要先了解什么是索引存储结构?

  • 索引存储结构就是在存储数据的同时建立一个附加索引表;
  • 索引表中的每一项称之为索引项;
  • 索引表中的键值对就是:关键字和地址,其中关键字是唯一标识;

分块查找的基本思路:

  • 将查找的表分为若干个子块,块内元素可以无序;
  • 索引表是有序的,一个块中最大的关键字小于第二块中所有关键字;

有如下一个数组: 

 要进行分块查找,首先我们要对这个数组建立一个索引表,注意!索引表规则是人定的,以区间  方式分割,这里我们发现可以把数组中的元素按照超过该元素的最小十位数的的规则来划分:

 把数据放进索引中如下图所示:

 这样索引表就建好了,我们选取索引表中每一块的第一个入表元素作为这一块的键值,并且用一个变量记录键值在数组中的下标;

#define MAXNUM 4
struct index
{
    int key;//键值
    int start;//开始的下标
} newIndex[MAXNUM];
  • 而当我们要查找某一个元素时,只需要遍历索引表中的每一块的键值,并且判断和待查值的大小关系;
  • 根据索引表的特性:一个块中最大的关键字小于第二块中所有关键字,就可以找到待查值所在的块了(通过顺序查找或二分查找);
  • 随后我们只需要在这个块中依次遍历就能找到待查值了;

源码,(代码不成熟,仅供参考

#include <stdio.h>
#include <stdlib.h>
#define MAXNUM 4

struct index
{
    int key;//键值
    int start;//开始的下标
} newIndex[MAXNUM];

void creatIndex(int a[], int n)
{
    for (int i = 0; i < 4; i++)
    {
        int min = 65535;
        for (int j = 0; j < n; j++)
        {
            if (a[j] > i * 10 && a[j] <= (i + 1) * 10)
            {
                if (a[j] < min)
                {
                    min = a[j];
                    newIndex[i].key = a[j];
                    newIndex[i].start = j;
                }
            }
        }
    }
}

int search(int a[], int key)
{
    int i = 0;
    //这里实际上是要小于分块的值
    //通过这个循环找到待查数据所在的区间
    while (i < 4 && key > newIndex[i].key)
    {
        i++;
    }
    //不i--的话没法搜素边缘的值
    i--;
    if (i >= 4)
    {
        return -1;
    }

    int startValue = newIndex[i].start;
    while (startValue <= newIndex[i + 1].start && a[startValue] != key)
    {
        startValue++;
    }
    if (startValue > newIndex[i + 1].start)
    {
        return -1;
    }
    return startValue;
}

int main()
{
    int arr[] = {9, 10, 13, 15, 19, 20, 27, 29, 31, 35};
    int len = sizeof(arr) / sizeof(arr[0]);
    creatIndex(arr, len);
    int pos = search(arr, 35);
    printf("%d", pos);
    system("pause");
    return 0;
}

 缺点:要用额外的空间建立索引表;

优点:建立索引表后的增删查改简单,并且查找速度快;

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小白还在写代码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值