18 线性表的查找

news2024/9/26 3:31:16

查找的基本概念

**查找:**在数据集合中寻找满足某种条件数据元素的过程,称之为查找。

  • 查找的结果 分别两种:
    • 查找成功 即在数据集合中找到了满足条件的数据元素。查找结果给出整个记录的信息,或者改记录在查找表中的位置
    • 查找失败 查找记录给出空指针空记录

查找表(查找结构):是由同一类型的数据元素(或记录)组成,可以是一个数组或链表…

  • 由于”集合“中的元素之间存在着松散的关系(没有严格的前取后继关系),因此查找表是一种应用灵活的结构。

关键字:数据元素中唯一标识该元素的某个数据项的值,使用基于关键字的查找,查找结果应该是唯一的。

例如:在有一个学生元素构成的数据集合中,学生元素中“学号”这一数据项的值唯一的标识一名学生。

  • 次关键字:反之:用以识别若干记录的关键字是次关键字。

    ​ 例如:用张三这个名字查找,可能有好几个同学

查找的目的:(一般操作)

  1. 查询:查询某个特定的数据元素是否在查找表中。
  2. 检索:检索某个特定的数据元素的各种属性。
  3. 插入:在查找表中插入一个数据元素。
  4. 删除:删除查找表中的某个数据元素。

动态查找表和静态查找表

  • 静态:若一个查找表的操作致设计上述目的中的1和2,则无须动态的修改查找表,此类查找表成为静态查找表。
  • 动态:与此对应,需要动态地修改查找表,称为动态查找表。

另外,适合静态查找表的查找方式有顺序查找,折半查找,散列查找等;

适合动态查找表的查找方式有二叉排序树的查找,散列查找等。

**平均查找长度:**即所有查找过程中进行关键字的比较次数的平均值,英文表示为ASL(Average Search Length),其数学定义为:

image-20230503000138409

其中,ASL 值 ↓,时间效率 ↑。

  • n:查找表中,总共有多少个数据记录。
  • pi:每一条数据记录可能被查找到的概率(通常认为 pi = 1/n)
  • ci:找到第 i 个数据元素所需要的比较次数。

平均查找长度是衡量查找算法效率的最主要的指标

线性表的查找

顺序查找(线性查找)

应用范围:
  • 顺序查找适用于顺序表或线性链表表示的静态查找表。
  • 表内元素之间可以是无序的,即查找表中元素的排列顺序并不影响查找过程。
  • 顺序查找在小规模数据集和查找表结构不明确的情况下可能是一个合适的选择。

ADT:

  1. 数据元素类型定义:
typedef struct {
    KeyType key; // 关键字域
    InfoType otherinfo; // 其他域
} ElemType;
  1. 顺序表定义:
// 顺序表结构类型定义
typedef struct {
    ElemType* R; // 存储空间基地址
    int length; // 记录当前表中有多少个数据元素
} SSTable; // Sequential Search Table
  1. 顺序查找算法实现:
// 顺序查找(线性查找)算法实现
int SequentialSearch(SSTable ST, KeyType key) {
    // 在顺序表St中查找关键字为key的元素,若找到则返回位置,否则返回-1
    ST.elem[0] = key; // 设置哨兵
    int i;
    for (i = ST.length; ST.elem[i] != key; --i);  //从后往前找,注意这里没有循环体,是空语句。
   	return i; // 若表中不存在关键字key的元素,将查找到i为0退出for循环
}

**引入哨兵的作用:**上述算法中,将ST.elem[0]称为哨兵,引入它的目的是使得Search_Seq内的循环不必判断数组是否越界。算法从尾部开始查找,若找到ST.elem[i]==key则返回 i 值,查找成功。否则一定在查找到ST.elem[0]==key时跳出循环,此时返回的是0,查找失败。在程序中引入“哨兵”并不是这个算法独有的,引入“哨兵”可以避免很多不必要的判断语句,从而提高程序效率。

对于有n个元素的表,给定值key与表中第 i 个元素相等,即定位第 i 个元素时,需进行 n-i+1 次关键字的比较,即Ci = n-i+1。查找成功时,顺序查找的平均长度为:ASL成功= ∑ i = 1 n \sum_{i=1}^n i=1nPi(n-i+1)

当每个元素的查找概率相等,即Pi =1/n时,有:ASL成功= ∑ i = 0 n \sum_{i=0}^n i=0nPi(n-i+1)= n + 1 2 \frac{n+1}{2} 2n+1

查找不成功时,与表中各关键字的比较次数显然是n+1次,即ASL不成功=n+1

通常,查找表中记录的查找概率并不相等。若能预先得知每个记录的查找概率,则应先对记录的查找概率进行排序,使表中记录按查找概率由大至小重新排列。

顺序查找算法的时间复杂度为O(n),其中n表示查找表的元素个数。在最好的情况下,需要查找1次即可找到目标元素;在最坏的情况下,需要查找n次。由于顺序查找的实现较简单,所以在小规模数据集或查找表结构不明确的情况下,顺序查找可能是一个合适的选择。然而,在大规模数据集或有序查找表的情况下,其他高效的查找方法(如二分查找、插值查找、哈希查找等)可能更为合适。

问题:

  1. 记录的查找概率不相等时,如何提高查找概率?
    • 查找表存储记录原则——按查找概率从高到低存储。
      • 查找概率越高,比较次数越少。
      • 查找概率越低,比较次数越高。
  2. 记录的查找概率无法测定时如何提高查找概率?
    • 方法——按查找概率动态调整记录顺序:
      1. 在每个记录中设一个访问频率域
      2. 始终保持记录按非递增有序的次序排列
      3. 每次查找后均将刚查到的记录直接移值表头。

顺序查找的特点

  • 优点:算法简单,逻辑次序无要求,且不同存储结构均适用(顺序/链式存储均可)。
  • 缺点:平均查找长度 ASL 太长,时间效率很低。

折半查找(二分查找)

二分查找(折半查找 ),它的主要思想是每次将待查记录所在区间缩小一半。该算法仅适用于已排序的数据。

非递归算法

查找过程:

  1. 设置查找区间的初始值:low 指向第一个元素,high 指向最后一个元素。

  2. 当 low <= high 时,执行以下操作:

    a. 计算中间值 mid:mid = (low + high) / 2

    b. 比较待查找关键字与 mid 位置的值:

    • 如果关键字等于 mid 位置的值,查找成功。
    • 如果关键字小于 mid 位置的值,更新 high = mid - 1,缩小查找范围到 mid 左边。
    • 如果关键字大于 mid 位置的值,更新 low = mid + 1,缩小查找范围到 mid 右边。
  3. 当 high < low 时,查找结束,表示查找失败。

二分查找的性能分析:

  1. 查找成功时的比较次数等于路径上的节点数,可以用二叉树来描述。
  2. 查找不成功时的比较次数等于路径上的内部节点数,最大次数为 log₂n + 1。
  3. 平均查找长度 ASL(成功时)表示查找成功时的平均比较次数。

二分查找的特点:

优点:查找效率高于顺序查找。 缺点:

  1. 只适用于有序表。
  2. 限于顺序存储结构(对线性链表无效)。

代码实现

#include <stdio.h>

// 二分查找函数
// 参数:arr - 已排序的整数数组,n - 数组长度,key - 要查找的关键字
// 返回值:如果找到关键字,返回对应的数组下标;否则返回 -1
int binary_search(int arr[], int n, int key) {
    int low = 0;        // 查找区间的下限,初始化为数组首元素下标
    int high = n - 1;   // 查找区间的上限,初始化为数组末元素下标
    int mid;            // 中间位置下标

    // 当 low <= high 时,继续查找
    while (low <= high) {
        mid = (low + high) / 2; // 计算中间位置下标

        // 比较关键字与中间位置的值
        if (key == arr[mid]) {
            return mid; // 查找成功,返回对应下标
        } else if (key < arr[mid]) {
            high = mid - 1; // 更新上限,缩小查找范围到 mid 左边
        } else {
            low = mid + 1; // 更新下限,缩小查找范围到 mid 右边
        }
    }

    // 查找失败,返回 -1
    return -1;
}

int main() {
    int arr[] = {1, 3, 5, 7, 9, 11, 13, 15}; // 已排序的整数数组
    int n = sizeof(arr) / sizeof(arr[0]);   // 计算数组长度
    int key = 7;                            // 要查找的关键字

    int result = binary_search(arr, n, key); // 调用二分查找函数

    if (result != -1) {
        printf("找到关键字 %d 在数组中的下标为 %d\n", key, result);
    } else {
        printf("未找到关键字 %d\n", key);
    }

    return 0;
}

折半查找(二分查找)性能分析——判定树

折半查找过程可以用二叉树来判断

树中的每一个结点对应表中的一个记录,但结点值不是记录的关键字,而是记录在表中的位置序号(或者是一份有序的关键字的值),树中最下面叶节点都是方形的,它表示查找不成功的情况。把当前查找区间的中间位置作为根,左子表和右子表分别作为根的左子树和右子树,由此得到的二叉树称为折半查找的判定树。结点所在层数表示该位置的元素需要比较多少次。若有序列表中有n个元素,则对应的判定树有n个圆形非叶节点和n+1个方形的叶节点。显然,判定树是一颗平衡二叉树。

image-20230504094515503

查找成功时,比较次数等于路径上的结点数,或者等于结点所在的层数。查找不成功时,比较次数等于路径上的内部结点数,且比较次数小于等于log2 n +1。(其中n为表中元素的总数)

假设每个元素的查找概率相等,求查找成功时的平均查找长度。所有元素的比较次数相加然后除以结点数。

平均查找长度ASL成功

设表长 n = 2h-1, 则h = log2(n+1)(此时,判定树的深度为j的满二叉树),且表中每个记录的查找概率相等:Pi=1/n。

折半查找特点:

优点:折半查找的效率较高,比顺序查找快。

缺点:

  1. 只适用于有序表,对无序表不适用。
  2. 限于顺序存储结构,对线性链表无效。

代码实现

判定树的二分查找是用于解释和分析二分查找过程的一种模型。在实际编程中,我们并不会显式地构建一个判定树,而是使用类似于上述非递归算法代码示例中的算法逻辑来实现二分查找。

判定树是一个概念性的工具,帮助我们理解二分查找的性质和性能,例如查找成功时的平均查找长度 (ASL)。在实际的算法实现中,我们依然会使用简单的循环结构和边界更新逻辑,而不是创建一个真实的判定树数据结构。

分块查找

分块查找,它将数据集分为多个较小的块,并对每个块建立索引。该方法旨在提高查找效率,同时便于插入和删除操作。分块查找的过程包括以下几个步骤:

  1. 数据分块:首先,将数据集分成若干个较小的块。每个块内的元素可以是有序的,也可以是无序的。如果每个块内的元素是无序的,我们称之为分块有序。这意味着块与块之间是有序的,而块内的元素是无序的。
  2. 建立索引表:为了快速定位待查找元素所在的块,需要建立一个索引表。索引表中的每个记录包含该块中的最大关键字以及该块中第一个元素的位置。索引表本身应该是有序的,以便于在查找过程中使用顺序查找或二分查找。
  3. 查找过程:分块查找的过程分为两个阶段。首先,在索引表中确定待查找元素所在的块。这可以通过顺序查找或二分查找实现。然后,在确定的块内进行查找。由于块内元素可能是无序的,通常采用顺序查找方法。

image-20230504110218571

分块查找的平均查找次数取决于索引表的查找次数(Lb)和块内查找的次数(Lw)。整体而言,分块查找的效率通常高于顺序查找,但略低于二分查找。然而,分块查找在处理动态变化的数据时具有优势,因为插入和删除操作比较容易实现,无须进行大量的元素移动。对于分块查找的缺点也很明显,分块查找需要增加一个索引表的存储空间并对初识索引表进行排序运算。

  • 索引表的查找次数(Lb):假设数据集中共有 n 个元素,每个块包含 s 个元素,那么索引表中有 n/s 个记录。在查找过程中,我们可以使用顺序查找或二分查找来查找索引表。以二分查找为例,索引表的平均查找次数为 Lb = log₂(n/s + 1)。
  • 块内查找的次数(Lw):在确定待查找元素所在的块之后,需要在块内进行查找。由于块内元素可能是无序的,通常采用顺序查找方法。对于顺序查找,平均查找长度为 Lw = s/2。

代码实现

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

#define MAX 100
/*  block search algorithm implementation */

// define the data structure
typedef struct {
    int key;
}ElemType;

typedef struct {
    ElemType *elem;
    int length;
}SSTable;

typedef struct {
    int key;
    int start;
}Idx;

// Init the SSTable
void createTable(SSTable *ST,int a[],int n)
{
    // allocate the spece for the elem
    ST->elem = (ElemType *)malloc((n+1)*sizeof(ElemType));
    // assign the ST length
    ST->length = n;
    // put the data into the elem
    for(int i=1;i<=n;i++)
    {
        ST->elem[i].key = a[i-1];
    }
}

// Init the index table
void createIndex(Idx index[],SSTable ST, int s)
{
    int i,n=ST.length/s;
    // if not divisible,please add a new index
    if(ST.length%s)
        n++;
    // create the index table
    for(i=0;i<n;i++)
    {
        // record the every block start position
        index[i].start = i*s+1;
        // record the every block max value
        index[i].key = ST.elem[i*s+1].key;
    }
}

// block search algorithm
int blockSearch(SSTable ST, Idx index[], int key, int s)
{
    int i,j,n=ST.length/s;
    // if not divisible,please add a new index
    if(ST.length%s)
        n++;
    /* first step ---- search to key from index table to find the location of the block in which the key resides*/
    for(i=0;i<n && index[i].key<key;i++);
    // if the key is not in the table,return 0
    if(i>=n) return 0;

    /* second step ---- search to key in within the corresponding block*/
    // get the start position of the block
    int start = index[i-1].start;
    // search
    for(j=start;j<start+s && ST.elem[j].key!=key;j++);
    // if the key is not in the table,return 0
    if(j>=start+s) return 0;
    // if the key is in the table,return the position
    return j;
}

int main() {
    // 初始化有序数组
    int a[] = {3, 8, 15, 23, 37, 49, 56, 62, 73, 88};
    int n = sizeof(a) / sizeof(a[0]);

    // 创建有序表
    SSTable ST;
    createTable(&ST, a, n);

    // 计算块大小
    int s = sqrt(n);

    // 创建索引表
    Idx index[MAX];
    createIndex(index, ST, s);

    // 要查找的元素
    int key = 49;

    // 调用分块查找函数
    int pos = blockSearch(ST, index, key, s);

    // 输出查找结果
    if (pos != -1) {
        printf("Find %d at position %d.\n", key, pos);
    } else {
        printf("Not found %d.\n", key);
    }

    return 0;
}

三种线性表的查找的比较

顺序查找折半查找分块查找
ASL最大最小中间
表结构有序表、无须表有序表分块有序
存储结构顺序表、线性链表顺序表顺序表、线性链表

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/487688.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vivo积分任务体系的架构演进-平台产品系列05

作者&#xff1a;vivo 互联网平台产品研发团队- Mu JunFeng 积分体系作为一种常见营销工具&#xff0c;几乎是每一家企业会员营销的必备功能之一&#xff0c;在生活中随处可见&#xff0c;随着vivo互联网业务发展&#xff0c;vivo积分体系的能力也随之得到飞速提升&#xff0c;…

Java字符串的用法、原理、性能分析和总结

本文介绍了Java字符串的创建方式、拼接、比较、查找和替换等常见操作&#xff0c;并解析了Java字符串在内存中的存储方式及常量池的作用。文章还提到了Java字符串的性能问题&#xff0c;建议在进行大量的字符串操作时&#xff0c;应该尽量避免使用""号操作符和concat…

【模拟IC学习笔记】 Widlar电流源

原理分析 M3、M4工作在饱和区&#xff0c;M1、M2可以工作在饱和区也可以工作在亚阈值区。 两边电流相等&#xff0c;M3、M4尺寸相等&#xff0c;Vgs2&#xff1c;Vgs1&#xff0c;所以M2的尺寸应该比M1的尺寸大才能保证两边电流相等。K为M2和M1的尺寸比。 当M1和M2工作在饱和…

springboot集成Validated实现参数检验,分组校验,嵌套检验等

一&#xff0c;开发前工作 引入依赖 <dependency> <!--新版框架没有自动引入需要手动引入--><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency> 二&#xff0c…

spring之ioc初见

maven创建 mvn archetype:generate # 选择7&#xff0c;创建快速启动项目然后idea打开即可 idea创建 点击创建 使用IOC pom.xml添加依赖 <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><…

pytorch实战9:基于pytorch简单实现u-net

基于pytorch简单实现u-net 前言 ​ 最近在看经典的卷积网络架构&#xff0c;打算自己尝试复现一下&#xff0c;在此系列文章中&#xff0c;会参考很多文章&#xff0c;有些已经忘记了出处&#xff0c;所以就不贴链接了&#xff0c;希望大家理解。 ​ 完整的代码在最后。 本系列…

组件等比例放大——scale和zoom

scale和zoom的区别 zoom的缩放是相对于左上角的&#xff1b;而scale默认是居中缩放&#xff0c;可以通过transform-origin修改基准点zoom的缩放改变了元素占据的空间大小&#xff1b;而scale的缩放占据的原始尺寸不变&#xff0c;页面布局不会发生变化。对文字的缩放规则不一致…

K8s基础2——部署单Master节点K8s集群、切换containerd容器运行时、基本命令

文章目录 一、部署K8S集群方式二、kubeadm工具搭建K8s集群2.1 资源配置2.2 服务器规划2.3 搭建流程2.3.1 操作系统初始化2.3.2 安装docker容器引擎2.3.3 安装cri-dockerd2.3.4 安装kubeadm&#xff0c;kubelet和kubectl2.3.5 master节点初始化2.3.6 加入node节点2.3.7 部署网络…

《花雕学AI》29:5秒钟就能为你的想法想出新点子?ChatGPT新点子指令模型告诉你怎么做

引言 你有没有遇到过这样的情况&#xff0c;你想出了一个想法&#xff0c;但是不知道怎么扩展或改进它&#xff1f;你有没有想过有一个工具&#xff0c;可以帮你在短时间内为你的想法生成各种新的点子&#xff1f;如果你有这样的需求&#xff0c;那么你一定要了解ChatGPT。 C…

Verilog 锁相环参数动态自动生成,Xilinx MMCM 和 PLL 动态配置频率

版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明。 本文链接&#xff1a;https://blog.csdn.net/qq_46621272/article/details/130484100 Verilog 锁相环参数动态自动生成&#xff0c;Xilinx MMCM 和…

服务器中了勒索病毒,升级后的Malox勒索病毒特征,勒索病毒解密数据恢复

Mallox勒索病毒是网络上较为流行的勒索病毒&#xff0c;但是随着黑客加密技术的不断升级&#xff0c;Mallox勒索病毒的新升级版本Malox勒索病毒已经开始出现。Malox勒索病毒是一种最近在网络上广泛传播的恶意软件&#xff0c;其感染方式多种多样&#xff0c;主要以加密受害人的…

【五一创作】版本控制-从零开始学Git-02 Git中的基本概念与工作流程

前言 前面学习了版本控制系统和分布式版本控制系统-Git的相关入门知识【五一创作】版本控制-从零开始学Git-01什么是Git 一、Git中的最基本概念 注意:.git目录是一个隐藏文件夹&#xff0c;默认不可见,需要设置后才能显示出来。 二、Git状态与结构关系 2.1 三种状态 modif…

Zigbee 无线串口通信模块( DL-22 )

文章目录 一、DL-22简介二、模块配置三、串口通信(透明传输) 一、DL-22简介 DL-22无线串口模块为串口转2.4G无线模块&#xff0c;可以通过无线将两个或者多个串口连接起来。串口发入模块的数据会被模块使用无线发出&#xff0c;收到无线数据的模块会将这个数据使用串口发出&am…

大数据分析案例-基于XGBoost算法构建居民收入分类预测模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

互联网明厨亮灶智慧监管算法 yolov7

互联网明厨亮灶智慧监管系统基于pythoyolov7网络模型AI视觉图像分析技术&#xff0c;互联网明厨亮灶智慧监管算法模型可以识别人员行为及穿戴是否合规&#xff0c;不穿厨师服、不按要求穿戴厨师帽或者佩戴口罩和手套、行为如违规在后厨抽烟、出现老鼠等情景。近几年来&#xff…

数字化转型导师坚鹏:数字化时代企业管理变革与创新营销

数字化时代企业管理变革与创新营销 课程背景&#xff1a; 很多饮水机企业存在以下问题&#xff1a; 不清楚数字化时代企业面临的机遇与挑战&#xff1f; 不知道如何利用大数据发现问题与机会&#xff1f; 不清楚如何进行精准的数字化营销&#xff1f; 不知道如何进行…

【ChatGPT】ChatGPT+MindShow三分钟生成PPT

Yan-英杰的主页 悟已往之不谏 知来者之可追 C程序员&#xff0c;2024届电子信息研究生 目录 第一步&#xff1a;获取PPT内容大概&#xff1a; 1.打开ChatGPT镜像 2.向他提问&#xff0c;提问格式如下&#xff0c;主题替换成你想获得的信息。比如某本书的拆解&#xff0c;…

数据可视化第二版-拓展-网约车分析案例

文章目录 数据可视化第二版-拓展-网约车分析案例竞赛介绍 1等奖作品-虾虾蟹蟹哈哈的作品引言&#xff1a;背景度量指标 & 结论过程数据与思考一、数据处理二、大盘存量数据一览1. 分城市关键指标变化情况2. 各项指标相关性分析 三、数量维度分析1. 城市间比较1.1 工作日 vs…

HTML和CSS

1、概述 超文本标记语言&#xff08;HyperText Markup Language&#xff09;简称&#xff1a;HTML&#xff1b;是一种用于创建网页的标准标记语言。HTML 不是一种编程语言&#xff0c;而是一种标记语言&#xff1b;HTML文档也叫做 web 页面。 运行后的浏览器页面 我的第一个标…

鲁大师4月安卓新机性能/流畅榜:ROG游戏手机7摘得性能桂冠 vivo登顶流畅榜

性能榜&#xff1a;ROG 游戏手机7众望所归&#xff0c;摘得性能桂冠 2023年4月的手机市场也是异常火爆&#xff0c;各大厂商纷纷推出自己的旗舰手机。骁龙8 Gen2仍然是厂商们的首选芯片&#xff0c;不论是“小米照相机”、vivo折叠机还是ROG 游戏机&#xff0c;他们都纷纷搭载了…