当前位置: 首页 > news >正文

数据结构(查找算法)

1. 查找的概念

在一堆数据中,找到我们想要的那个数据,就是查找,也称为搜索,很容易想到,查找算法的优劣,取决于两个因素:

  • 数据本身存储的特点
  • 查找算法本身的特点

比如,如果数据存储是无序的,那么当我们想要在其中找到某个节点时,一般就只能对它们逐个比对。如果数据存储是有序且存放在一片连续的内存中,那么我们可以考虑从中间开始找(二分法)。因此可以看到,在实际应用中如果需要优化数据的查找(搜索)性能,我们主要从以上两方面入手,当然,有时数据的存储特性是无法更改的,那么此时就只能靠优化算法本身去达到提供程序性能的目的了。

2. 经典查找算法

2.1 顺序查找

顺序查找顾名思义,就是挨个找,这是一种不是算法的“算法”,最没办法的“办法”,简单直接,童叟无欺。时间复杂度就是 O(n)

适用情况:

  • 无序的数据
  • 无法(或不便于)对这些数据进行有效的整理

以下是示例代码

// 数据规模:100万
#define SCALE 1000*1000// 查找次数统计
static int count;int sequentialSearch(unsigned data[], int len, int n)
{for(int i=0; i<SCALE; i++){if(data[i] == n)return i;count++;}return -1;
}int main(int argc, char const *argv[])
{// 产生一系列无序数据srand(time(NULL));unsigned *data = calloc(SCALE, sizeof(unsigned));for(int i=0; i<SCALE; i++)data[i] = rand()%((int)pow(10, rand()%8+5));// 不进行任何数据整理,直接进行顺序查找unsigned n;printf("请输入你要找的正整数:\n");while(1){scanf("%u", &n);int pos = sequentialSearch(data, SCALE, n);if(pos == -1)printf("找不到你要的数据");elseprintf("你要找的数据第%d行", pos);printf("【找了%d次】\n", count);count = 0;}return 0;
}

以下是执行效果:

gec@ubuntu: ~$ ./sequential_search
请输入你要找的正整数:
132096806
你要找的数据第87行【找了87次】
951578948
你要找的数据第999999行【找了999999次】
951578949
找不到你要的数据【找了1000000次】

很显然,除非数据完全无序且无法整理,否则顺序查找的效率在大规模数据面前是非常低下的,因此一般情况下,对于大数据量的查找,我们都会尽量先对数据进行不同程度的整理。

2.2 分块查找

在一本字典中查找某个汉字的时候,一般是先找目录,在目录中找到对应拼音或笔画,然后直接翻到对应的页面再往下找,很显然这能大大提高查找的效率。

分块查找实质就是给数据建立索引,将查找的过程分成两个步骤:

  1. 在索引(目录)中找到数据所属分块
  2. 在所属分块中查找到该数据

以下例子中,先是产生系列长度和内容都随机的字符串,作为待查数据集。然后对这些字符串按首字符进行整理,形成字母表索引(目录)。最后利用索引提高查找效率。

核心代码示例

// 数据规模: 100万
#define SCALE  1000*1000 // 字符串总数
#define MAXLEN 20        // 字符串最大长度// 查找次数统计
static int count;char *random_string()
{int len = rand()%(MAXLEN);len = ((len<2)? 2 : len); // len: 2~19char *s = calloc(1, len);char letter[] = {'a', 'A'};for(int i=0; i<len-1; i++) // i: 0~[1~18]s[i] = letter[rand()%2]+rand()%26;return s;
}void create_index(char data[][MAXLEN], int **index)
{// 统计各个首字符出现的频次int n[52]={0}; // ['a', 'b', ... 'z', 'A', 'B', ... 'Z']for (int k = 0; k < SCALE; k++){// 小写字母[00~25],大写字母[26~51]int pos = ((data[k][0] >= 'a') ? (data[k][0]-'a') : (data[k][0]-'A'+26));n[pos]++;}// 给index分配内存// 每个字母分配一段存储以该字母为首的字符串所在的行号的内存// 第一个位置存储总行数,因此所需分配的内存单元数是1+n[i]。// 例如:// index[2] --> [ 242     3     22    213 ... ... 42513 46698]//   'c'    -->  总行数  第3行 第22行     ... ...for(int i=0; i<52; i++)index[i] = calloc(1+n[i], sizeof(int));// 记录每个字母出现的行号for(int i=0; i<SCALE; i++){int pos = ((data[i][0] >= 'a') ? (data[i][0]-'a') : (data[i][0]-'A'+26));int k = ++index[pos][0];index[pos][k] = i;}
}int main(int argc, char const *argv[])
{// 1. 产生随机字符串数据集//    假设每个字符串长度不超过MAXLEN个字符char (*data)[MAXLEN] = calloc(SCALE, MAXLEN);srand(time(NULL));for(int i=0; i<SCALE; i++){char *s = random_string();strncpy(data[i], s, strlen(s));free(s);}// 2. 按首字母建立索引(分块)int **index = calloc(52, sizeof(int *));create_index(data, index);// 3. 利用索引,进行查找char str[32];printf("请输入你要查找的字符串:\n");while(1){// 从键盘接收一个待查找的字符串并去掉回车符bzero(str, 32);fgets(str, 32, stdin);strtok(str, "\n");bool done = false;for(int i=1; i<SCALE; i++){// 小写字母[00~25],大写字母[26~51]int pos = ((str[0]>='a') ? (str[0]-'a') : (str[0]-'A'+26));count++;if(i<=index[pos][0] && strcmp(data[index[pos][i]], str) == 0){printf("你要找的字符串在第%d行", index[pos][i]);done = true;break;}else if(i > index[pos][0])break;}if(!done)printf("没有你要的字符串");printf("【找了%d次】\n", count);count=0;}return 0;
}

以下是执行效果:

gec@ubuntu: ~$ ./index_search
请输入你要查找的字符串:
e
你要找的字符串在第8行【找了1次】
sdf
你要找的字符串在第206096行【找了4078次】
ssHcbSJC
你要找的字符串在第396828行【找了7891次】

由于对数据进行了分块,引入了索引,查找效率大大提高,对于上述的100万个数据来说,假设以各个大小写字母开头的字符串概率相等,那么相当于将整体数据分成了52块,整体效率平局提升52倍,这个性能的提升的相当显著的,所付出的代价是:需要额外的内存空间存储索引表index,这是以空间换时间的经典思路。

2.3 二分查找

分块查找是在不对数据进行排序的情况下采用的颇为有效的查找办法,但如果待查找的数据本身是有序的,或者在查找前,可以对数据先进行排序(比如数据量虽然较大,但短期较稳定,无大面积更新),这种情况下使用二分查找可以进一步提升效率。

二分法的思路相当朴实无华:从中间开始找。既然数据是有序的,那么如果将待查找的节点跟中间节点对比,就可以以排除掉一半的数据,接着再在剩余的数据的中间开始找,又可以很快排除掉剩下的一半的数据,这种一半一半筛查数据的办法,就是所谓的二分法。

例如下图,想要在一系列有序(从小到大)的数据中,查找45,中间的节点数据是22,很显然左侧数据可以立即排除,45只可能存在于22右侧的序列中(若有):

img
二分查找算法示意图

假设有一系列有序数据,以下是二分查找算法的核心代码

// 数据规模: 100万
#define SCALE  1000*1000// 查找次数统计
static int count;int main(int argc, char const *argv[])
{// 1. 产生SCALE个随机整数序列unsigned *data = calloc(SCALE, sizeof(unsigned));srand(time(NULL));for(int i=0; i<SCALE; i++)data[i] = rand()%((int)pow(10, rand()%8+5));// 2. 排序quick_sort(data, SCALE);// 3. 进行二分查找unsigned n;printf("请输入你要查找的正整数:\n");while(1){scanf("%u", &n);int low, high, mid;low = 0, high = SCALE-1;bool found = false;while(low <= high){count++;mid = (low+high)/2;if(n == data[mid]){printf("你要找的数据在第%d行", mid);found = true;break;}if(n < data[mid])high = mid - 1;elselow = mid + 1;} if(!found)printf("找不到你要的数据");printf("【找了%d次】\n", count);count=0;}return 0;
}

以下是执行效果:

gec@ubuntu: ~$ ./binary_search
请输入你要查找的正整数:
1
你要找的数据在第6行【找了17次】
534
你要找的数据在第731行【找了12次】
6996
你要找的数据在第9534行【找了16次】
5863827
你要找的数据在第333334行【找了20次】

可见,二分查找的查找效率得到了质的飞跃!在最不利的情况下,100万个数据最多仅需查找20次就能锁定结果,这个效率大大优于顺序查找和分块查找。但别忘记,适合于用二分查找的场合是有条件的:数据是严格有序的。

相关文章:

数据结构(查找算法)

1. 查找的概念 在一堆数据中&#xff0c;找到我们想要的那个数据&#xff0c;就是查找&#xff0c;也称为搜索&#xff0c;很容易想到&#xff0c;查找算法的优劣&#xff0c;取决于两个因素&#xff1a; 数据本身存储的特点查找算法本身的特点 比如&#xff0c;如果数据存储…...

private前端常见算法

1.数组 合并两个有序数组&#xff08;简单-5&#xff09; https://leetcode.cn/problems/merge-sorted-array/description/?envTypestudy-plan-v2&envIdtop-interview-150 移除元素&#xff08;简单-4&#xff09; https://leetcode.cn/problems/remove-element/descr…...

Go语言之十条命令(The Ten Commands of Go Language)

Go语言之十条命令 Go语言简介 Go语言&#xff08;又称Golang&#xff09;‌是由Google开发的一种开源编程语言&#xff0c;首次公开发布于2009年。Go语言旨在提供简洁、高效、可靠的软件开发解决方案&#xff0c;特别强调并发编程和系统编程‌。 Go语言的基本特征 ‌静态强类…...

Residency 与 Internship 的区别及用法解析

Residency 与 Internship 的区别及用法解析 在英文中&#xff0c;“residency” 和 “internship” 都与职业培训相关&#xff0c;但它们的使用场景和具体含义存在显著差异。本文将详细解析这两个词的区别&#xff0c;以及它们在不同语境下的应用。 Residency 的定义及使用场景…...

成品电池综合测试仪:电子设备性能与安全的守护者|鑫达能

在现代科技和工业领域&#xff0c;电池作为能量储存和转换的关键组件&#xff0c;其性能的稳定性和可靠性至关重要。为了确保电池在各种应用场景中都能发挥最佳性能&#xff0c;成品电池综合测试仪应运而生。这一设备不仅能够对电池的各项性能指标进行全面、准确的检测&#xf…...

Taro地图组件和小程序定位

在 Taro 中使用腾讯地图 1.首先在项目配置文件 project.config.json 中添加权限&#xff1a; {"permission": {"scope.userLocation": {"desc": "你的位置信息将用于小程序位置接口的效果展示"}} }2.在 app.config.ts 中配置&#x…...

深入了解 SSL/TLS 协议及其工作原理

深入了解 SSL/TLS 协议及其工作原理 一. 什么是 SSL/TLS?二. SSL/TLS 握手过程三. SSL/TLS 数据加密与传输四. 总结 点个免费的赞和关注&#xff0c;有错误的地方请指出&#xff0c;看个人主页有惊喜。 作者&#xff1a;神的孩子都在歌唱 一. 什么是 SSL/TLS? 安全套接层&am…...

【计算机操作系统:二、操作系统的结构和硬件支持】

第2章 操作系统的结构和硬件支持 2.1 操作系统虚拟机 操作系统虚拟机是一种通过软件技术对硬件资源进行抽象和虚拟化的机制&#xff0c;使用户能够以逻辑方式访问和使用计算机资源。 定义与概念&#xff1a; 虚拟机是操作系统虚拟化技术的核心产物&#xff0c;通过模拟硬件资…...

51单片机——步进电机模块

直流电机没有正负之分&#xff0c;在两端加上直流电就能工作 P1.0-P1.3都可以控制电机&#xff0c;例如&#xff1a;使用P1.0&#xff0c;则需要把线接在J47的1&#xff08;VCC&#xff09;和2&#xff08;OUT1&#xff09;上 1、直流电机实验 要实现的功能是&#xff1a;直…...

当算法遇到线性代数(四):奇异值分解(SVD)

SVD分解的理论与应用 线性代数系列相关文章&#xff08;置顶&#xff09; 1.当算法遇到线性代数&#xff08;一&#xff09;&#xff1a;二次型和矩阵正定的意义 2.当算法遇到线性代数&#xff08;二&#xff09;&#xff1a;矩阵特征值的意义 3.当算法遇到线性代数&#xff0…...

SASS 简化代码开发的基本方法

概要 本文以一个按钮开发的实例&#xff0c;介绍如何使用SASS来简化CSS代码开发的。 代码和实现 我们希望通过CSS开发下面的代码样式&#xff0c;从样式来看&#xff0c;每个按钮的基本样式相同&#xff0c;就是颜色不同。 如果按照传统的方式开发&#xff0c;需要开发btn &…...

40.TryParse尝试转化为int类型 C#例子

也许这个时候学有点晚&#xff0c;但是不管怎样都学了 尝试转化&#xff0c;不能转化就返回bool类型的假 它会直接给括号里面的int类型赋值 代码&#xff1a; using System; using System.Timers; public class Program {static void Main(){int a;bool i;while (true){Get…...

【微服务】2、网关

Spring Cloud微服务网关技术介绍 单体项目拆分微服务后的问题 服务地址问题&#xff1a;单体项目端口固定&#xff08;如黑马商城为8080&#xff09;&#xff0c;拆分微服务后端口各异&#xff08;如购物车808、商品8081、支付8086等&#xff09;且可能变化&#xff0c;前端难…...

红队-shell编程篇(上)

声明 通过学习 泷羽sec的个人空间-泷羽sec个人主页-哔哩哔哩视频,做出的文章如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 一、建立Shell文件 1. Shell简介 Shell是一种命令行界面&am…...

电子价签会是零售界的下一个主流?【新立电子】

电子价签&#xff0c;作为一种能够替代传统纸质标签的数字显示屏&#xff0c;已经在零售行业中展现出其巨大的潜力。它具有实时更新、集中管理、高效节能的特点&#xff0c;实现价格的实时更新&#xff0c;大大减少更新价格的工作量和时间。为消费者带来更加便捷、准确的购物体…...

5 分布式ID

这里讲一个比较常用的分布式防重复的ID生成策略&#xff0c;雪花算法 一个用户体量比较大的分布式系统必然伴随着分表分库&#xff0c;分机房部署&#xff0c;单体的部署方式肯定是承载不了这么大的体量。 雪花算法的结构说明 如下图所示: 雪花算法组成 从上图我们可以看…...

SpringBoot | @Autowired 和 @Resource 的区别及原理分析

关注&#xff1a;CodingTechWork 引言 在Spring框架中&#xff0c;Autowired 和 Resource 是两种常用的依赖注入注解&#xff0c;它们都用于自动装配Bean&#xff0c;简化了开发者手动创建和管理Bean的繁琐工作。然而&#xff0c;它们的实现机制和使用方式有所不同。理解这两者…...

『SQLite』解释执行(Explain)

摘要&#xff1a;本节主要讲解SQL的解释执行&#xff1a;Explain。 在 sqlite 语句之前&#xff0c;可以使用 “EXPLAIN” 关键字或 “EXPLAIN QUERY PLAN” 短语&#xff0c;用于描述表查询的细节。 基本语法 EXPLAIN 语法&#xff1a; EXPLAIN [SQLite Query]EXPLAIN QUER…...

0基础学前端-----CSS DAY12

视频参考&#xff1a;B站Pink老师 今天是CSS学习的第十二天&#xff0c;今天开始的笔记对应Pink老师课程中的CSS第七天的内容。 本节重点&#xff1a;CSS高级技巧 本章目录 本节目标1. 精灵图1.1 为什么需要精灵图1.2 精灵图使用案例&#xff1a;拼出自己的名字 2. 字体图标2.…...

(概率论)无偏估计

参考文章&#xff1a;(15 封私信 / 51 条消息) 什么是无偏估计&#xff1f; - 知乎 (zhihu.com) 首先&#xff0c;第一个回答中&#xff0c;马同学图解数学讲解得很形象&#xff0c; 我的概括是&#xff1a;“注意&#xff0c;有一个总体的均值u。然后&#xff0c;如果抽样n个&…...

Minio-Linux-安装

文章目录 1.Linux安装1.下载源码包2.上传到/usr/local/minio1.进入目录2.上传 3.开放执行权限4.创建minio文件存储目录及日志目录5.编写启动的shell脚本1.脚本编写2.赋予执行权限 6.启动&#xff01;1.执行run脚本2.查看日志3.开放9001和9000端口1.服务器2.安全组3.访问&#x…...

利用Java爬取1688商品详情API接口:技术与应用指南

引言 1688作为中国领先的B2B电子商务平台&#xff0c;拥有海量的商品信息。对于商家和市场研究人员来说&#xff0c;能够从1688获取商品详情信息&#xff0c;对于市场分析、竞品研究等具有重要价值。本文将详细介绍如何使用Java编写爬虫程序&#xff0c;以合法、高效的方式获取…...

基于MATLAB的汽车热管理模型构建

一、引言 汽车热管理系统对汽车性能、部件寿命及驾乘体验至关重要。它能确保发动机、电池等关键部件在适宜温度工作。MATLAB 功能强大&#xff0c;为构建高精度热管理模型提供有效途径&#xff0c;助力优化系统设计与控制策略。 二、汽车热管理系统构成 2.1 发动机冷却系统&…...

LRU(1)

LRU是"Least Recently Used"(最近最少使用)的缩写&#xff0c;它是一种常用的页面置换算法和缓存淘汰策略。当计算机系统的内存或缓存资源有限时&#xff0c;LRU算法根据的历史访问记录来决定哪些数据应该被保留在内存或缓存中&#xff0c;哪些被淘汰。其核心思想是“…...

VSCode 使用鼠标滚轮控制字体

一、 文件 | 首选项 | 设置 二、单击在 settings.json中编辑 "editor.mouseWheelZoom": true 注注注意&#xff1a;保存哦&#xff01;ctrlS 三、测试 按住ctrl鼠标滚轮&#xff0c;控制字体大小...

数据库(3)--针对列的CRUD操作

1.Create 新增 语法&#xff1a; insert into 表名 &#xff08;列名&#xff09;values &#xff08;列&#xff09;... 创建一个学生表用于演示&#xff1a; create table if not exists student( id bigint comment 编号, name varchar(20) comment 姓名 ); 1.1直接增加…...

【Linux】记录一下考RHCE的学习过程(七)

年底了&#xff0c;公司接的北京地铁轨道交通的项目做不完了&#xff0c;一百多列地铁的设备都得调&#xff0c;派我出差了几周&#xff0c;这几天才回来&#xff0c;出差累死了实在是没办法更新。&#xff08;YOASOBI的二开票还没抢到ToT&#xff0c;哭死&#xff0c;看看回滚…...

【顶刊TPAMI 2025】多头编码(MHE)之极限分类 Part 1:背景动机

目录 1 简单概括2 几个重要发现3 主要贡献4 背景知识5 方法简介 论文&#xff1a;Multi-Head Encoding for Extreme Label Classification 作者&#xff1a;Daojun Liang, Haixia Zhang, Dongfeng Yuan and Minggao Zhang 单位&#xff1a;山东大学 代码&#xff1a;https://gi…...

使用hardhat进行合约测试

演示源码&#xff1a;hardhat-demo: 演示基于hardhat的HelloWord合约测试案例。 环境 NodeJs 创建工程 1.创建一个hardhat工程根目录(hardhat-demo)&#xff0c;然后进入该目录执行。 npx hardhat执行该命令&#xff0c;会进行hardhat工程初始化。 提示我们是否安装该版本h…...

基于生成式对抗网络(GAN)的前沿研究与应用

引言 人工智能&#xff08;AI&#xff09;领域在过去几年中经历了快速的发展&#xff0c;尤其是深度学习的兴起带来了许多变革。其中&#xff0c;生成式对抗网络&#xff08;Generative Adversarial Network, GAN&#xff09;因其强大的生成能力成为了研究热点。自2014年Ian G…...