查找算法及哈希表

1 二分查找

1.1 重要概念
  • 拟解决的问题:判断某个区间是否包含某个元素,无法确定区间中包含重复元素的具体位置;
  • 使用条件:查找的区间必须符合单调性;
  • 本质:采用分治思想,将某个单调区间一分为二,保证留下的一半区间包含解,舍弃的一半区间不包含解;
  • 时间复杂度:O(log2n)�(���2�)
  • 计算方式:二分查找每查找一次将原问题的规模n缩减到1/2,最糟糕的情况为n=1时,二分查找获得结果,此时二分查找的次数为$n/2^x=1,即,即x = log_2n$
1.2 应用场景
  • 判断某个单调区间是否包含某个元素;
  • 前面一堆1,后面一堆0,如1111100000,查询最后一个1出现的位置;(特殊情况1)
  • 前面一堆0,后面一堆1,如0000011111,查询第一个1出现的位置;(特殊情况2)

1.3 代码演示
// 1 3 5 7 9 10
int binary_search(int *arr, int n ,int x) {int head = 0, tail = n - 1, mid;while (head <= tail) {mid = (head + tail) >> 1;if (arr[mid] == x) return mid;if (arr[mid] < x) head = mid + 1;else tail = mid - 1;}return -1;
}// 1111100000
int binary_search1(int *arr, int n) {int head = -1, tail = n - 1, mid;   // 当查找区间的元素全0时,为避免二义性,定义head=-1,而不是head=0while (head < tail) {mid = (head + tail + 1) >> 1;   // 上取整,否则会出现死循环if (arr[mid] == 1) head = mid;  // mid 有可能是最后一个1出现的位置else tail = mid -1;}// head = tail = -1 时,代表未找到,否则返回对应元素的位置return head;
}// 00000111111
int binary_search2(int *arr, int n) {int head = 0, tail = n, mid;        // 当查找区间的元素全0时,为避免二义性,定义tail=n,而不是tail=n-1while (head < tail) {mid = (head + tail) >> 1;if (arr[mid] == 1) tail = mid;  // mid 有可能是第一个1出现的位置else head = mid + 1;}// head = tail = n 时,代表未找到,否则返回对应元素的位置return head == n ? -1 : head;
}

2 三分查找

2.1 拟解决的问题

二分查找解决的是在单调序列中查找目标值的问题,而三分查找则是确定函数在凹/凸区间上的极值点;

2.2 算法描述
    在函数f(x)的某个区间[l, r]上取2个分界点,其中m1位于l的1/3处,m2位于l的2/3处,即:$m1 = l + (r - l) / 3$,$m2 = r - (r - l) / 3$,这两个点m1、m2把区间 [l, r] 分为3 个子区间。这里以凸函数(即有最大值)为例讨论:
  • 若f(m1) < f(m2),说明极值点位于[m1, r]区间内,可以不必再考虑[l, m1]区间;原因就是当f(m1) < f(m2)时,m1处于单调递增区间段,故f(l) < f(m1);
  • 若f(m1) > f(m2),说明极值点位于[l, m2]区间内,可以不必再考虑[m2, r]区间;原因就是当f(m1) > f(m2)时,m2处于单调递减区间段,故f(m2) > f(r);
  • 这样,每一轮迭代都会把查找范围限制在原来的2/3,直到最终逼近极值点,即l和r之间的差值接近无穷小;
  • 三分查找的时间复杂度:O(log3n)�(���3�),二分查找的时间复杂度:O(log2n)�(���2�),尽管二者的复杂度级别一样,但是二分查找的效率更高,因为二分查找是在1/2的区域寻找值,而三分查找是在2/3的区域寻找值;
  • 参考博客:三分查找(ternary search)及其示例 - 简书、二分查找和三分查找哪个快?算法复杂度与常数无关?复杂度分析的常见误区 - 知乎

2.3 代码演示
double three_point_search(double (*func)(double), double l, double r) {double m1, m2;int flag = 0; // flag = 0:func 函数为凸函数;flag = 1:func 函数为凹函数// 凹凸函数判断if (func((l + r) / 2.0) > (func(l) + func(r)) / 2.0 ) flag = 0;else flag = 1;#define EPSL 1e-6if (!flag) {while (fabs(r - l) > EPSL) {m1 = l + (r - l) / 3.0, m2 = r - (r - l) / 3.0;if (func(m1) < func(m2)) l = m1;else r = m2;}} else {while (fabs(r - l) > EPSL) {printf("l = %f, r = %f\n", l, r);m1 = l + (r - l) / 3.0, m2 = r - (r - l) / 3.0;if (func(m1) < func(m2)) r = m2;else l = m1;}}#undef EPSLreturn l;
}

3 哈希表 HashTable

3.1 介绍
   基于数组的“随机访问”特性,其可以通过下标快速地找到对应位置的元素,然而这种通过`<int>`下标寻找`<任意类型>`元素的映射关系并不通用(映射关系:`<int>`→`<任意类型>`)。所以,为了拥有数组“快速访问”的特性,以及在查找元素过程中具有<`任意类型`>到`<任意类型>`的映射关系,哈希表就应运而生了。哈希表主要由**哈希函数** 和 **哈希冲突处理方法**两部分组成,其中**哈希函数**完成<`任意类型`>到`<int>`的映射(这儿用key-value来说明,key为输入,value为输出,即key到value的映射),但是这种映射关系并不唯一,即不同的key可能会有相同的value存在,若直接通过value来标记key的话,就会有覆盖现象发生,此时就会用到 **哈希冲突处理方法**。简单来讲,哈希表(Hash table,也叫散列表),是通过哈希函数将任意类型的数据转化成一个整型数字,然后用这个整型数字对数组长度取余,将其结果作为数组的下标,然后把这组数据存储到对应下标的数组空间(**数据存储过程**)。在使用哈希表进行数据查询时,就是再次通过哈希函数获取数组的下标,然后使用这个下标直接访问对应位置的数组元素,故哈希表查找数据的时间复杂度几乎为O(1)(**数据查找过程**)。

  • 什么是哈希表(散列表)

    哈希表就是通过哈希函数将输入映射到数组的某个位置,然后利用数组的“随机访问”特性进行快速查找;通过哈希函数映射值来存放数据的数组就是哈希表。

  • 为什么要使用哈希表

    在几乎为O(1)的时间复杂度内快速查找元素在数组中的位置。与一般查找不同,若直接在数组内查找某个元素,需要从头遍历一次数组,其时间复杂度为O(n);若使用二分查找,每次都需要将待查找区间缩小一半,其时间复杂度为O(logn);

  • 哈希表、数组、链表的区别

    为了具有数组“随机访问”的特性,在哈希表中引入了哈希函数,然而哈希函数的引入会出现哈希冲突,比如“如果两个字符串在哈希表中对应的位置相同怎么办?”,而数组的容量又是有限的,其中一种解决方案就是使用链表结构,在哈希表的每个入口挂一个链表,保存所有对应的字符串就OK了,此时的哈希表就是一种数组+链表组合的数据结构。

3.2 哈希(散列)函数

把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

3.3 冲突处理方法
  • 开放定值
  • 再哈希|散列
  • 拉链法
  • 建立公共溢出区

哈希hash(散列)表结构详解_hash的结构-CSDN博客

Hash算法_bkdrhash acm-CSDN博客

3.4 代码演示
// 哈希函数:BKDRHash, 将 字符串类型 -> int
// 冲突处理:拉链法typedef struct Node {char *str;struct Node *next;
} Node;typedef struct HashTable {Node **data;int size;
} HashTable;Node *init_Node(char *str, Node *head) {Node *p = (Node *)malloc(sizeof(Node));p->str = strdup(str); // 深拷贝p->next = head;       // 头插法return p;
}HashTable *init_hash(int n) {HashTable *h = (HashTable *)malloc(sizeof(HashTable));h->size = n << 1; // 哈希表的空间利用率,一般为70%~90%,当前利用率为50%h->data = (Node **)calloc(h->size, sizeof(Node *));return h;
}int BKDRHash(char *str) {int seed = 31, hash = 0;for (int i = 0; str[i]; i++) hash = hash * seed + str[i];return hash & 0x7fffffff;
}int insert(HashTable *h, char *str) {int hash = BKDRHash(str);int ind = hash % h->size;h->data[ind] = init_Node(str, h->data[ind]);  // 拉链法return 1;
}int search(HashTable *h, char *str) {int hash = BKDRHash(str);int ind = hash % h->size;Node *p = h->data[ind];while (p && strcmp(p->str, str)) p = p->next;return p != NULL;
}void clear_node(Node *head) {if (head == NULL) return ;Node *p = head, *q;while (p != NULL) {q = p->next;free(p->str);free(p);p = q;}return ;
}void clear(HashTable *h) {if (h == NULL) return;for (int i = 0; i < h->size; i++) {clear_node(h->data[i]);}free(h->data);free(h);return ;
}

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

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

相关文章

12.2_黑马Redis实战篇附近商铺用户签到UV统计

实战篇11 实战篇12 要先用test的方式把商铺的数据导入到idea当中&#xff0c;才可以进行查询噢。 代码&#xff1a; 实战篇13 thinking&#xff1a;插件mavenhelper&#xff1f; 方便处理pom文件。 实战篇15 实战篇16 thinking&#xff1a;XX.format(DateTimeFormatter.ofP…

【网络安全技术】实体认证技术Kerberos

一、什么是Kerberos Kerberos解决的是客户端与服务器通信场景中&#xff0c;确保客户端服务器双方的身份可信&#xff0c;并提供对称密钥的分发来加密传输。是一个应用层的协议。 二、一个简单的模型 1.看这个基础的模型&#xff0c;客户端要和服务器通信&#xff0c;他先将自…

《堆》的模拟实现

目录 前言&#xff1a; 模拟实现《堆》&#xff1a; 1.自定义数据类型 2.初始化“堆” 3.销毁“堆” 4.进“堆” 关于AdjustUp() 5.删除堆顶元素 关于AdjustDown() 6.判断“堆”是否为空 7.求“堆”中的数据个数 8.求“堆”顶元素 总结&#xff1a; 前言&#xf…

DOM 事件的传播机制

前端面试大全DOM 事件的传播机制 &#x1f31f;经典真题 &#x1f31f;事件与事件流 事件流 事件冒泡流 事件捕获流 标准 DOM 事件流 &#x1f31f;事件委托 &#x1f31f;真题解答 &#x1f31f;总结 &#x1f31f;经典真题 谈一谈事件委托以及冒泡原理 &#x1f3…

SmartSoftHelp8数据库连接字符串强优化,高并发配置

1.设置数据库是否异步连接 2.数据库连接是否复用 3.最大链接数 4.最小连接数 5.等待时间 6.生命周期 下载地址&#xff1a; 百度网盘 请输入提取码

24、蜂鸣器

蜂鸣器介绍 蜂鸣器是一种将电信号转换为声音信号的器件&#xff0c;常用来产生设备的按键音、报警音等提示信号 蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器 有源蜂鸣器&#xff1a;内部自带振荡源&#xff0c;将正负极接上直流电压即可持续发声&#xff0c;频率固定 无源蜂…

【linux】信号——信号保存+信号处理

信号保存信号处理 1.信号保存1.1信号其他相关概念1.2信号在内核中的表示 2.信号处理2.1信号的捕捉流程2.2sigset_t2.3信号集操作函数2.4实操2.5捕捉信号的方法 3.可重入函数4.volatile5.SIGCHLD信号 自我名言&#xff1a;只有努力&#xff0c;才能追逐梦想&#xff0c;只有努力…

计算机毕业设计 基于SpringBoot的敬老院管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

数据结构与算法-静态查找表

&#x1f31e; “清醒 自律 知进退&#xff01;” 查找 &#x1f388;1.查找的相关概念&#x1f388;2.静态查找表&#x1f52d;2.1静态查找表的类定义&#x1f52d;2.2顺序查找&#x1f52d;2.3二分查找&#x1f50e;二分查找例题 &#x1f52d;2.4分块查找&#x1f52d;2.5三…

理解BatchNormalization层的作用

深度学习 文章目录 深度学习前言一、“Internal Covariate Shift”问题二、BatchNorm的本质思想三、训练阶段如何做BatchNorm四、BatchNorm的推理(Inference)过程五、BatchNorm的好处六、机器学习中mini-batch和batch有什么区别 前言 Batch Normalization作为最近一年来DL的重…

logistic回归详解

为什么不直接统计标签数和预测结果数&#xff0c;计算精度&#xff1f; 因为 存在梯度为0的情况梯度不连续 为什么叫logistic回归 logistic是因为加了一个sigmoid函数&#xff0c;将输出预测值映射到【0&#xff0c;1】 有时候使用MSE损失函数&#xff0c;拟合 有时候使用c…

selenium三猛士

selenium包括三个项目&#xff0c;分别是&#xff1a;Selenium WebDriver,Selenium IDE&#xff0c;Selenium Grid。 Selenium WebDriver Selenium WebDriver是客户端API接口&#xff0c;测试人员通过调用这些接口&#xff0c;来访问浏览器驱动&#xff0c;浏览器再访问浏览器…

利用Python中的Manim进行数学绘画和创作

相信很多同学就算没听过3Blue1Brown&#xff0c;也一定曾看过他们出品的视频&#xff0c;其从独特的视觉角度解说各种数学概念&#xff0c;内容包括线性代数、微积分、神经网络、傅里叶变换以及四元数等晦涩难懂的知识点。例如最火的《线性代数本质》系列视频。 那么这些视频是…

【算法】动态规划中的路径问题

君兮_的个人主页 即使走的再远&#xff0c;也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;如果给算法的难度和复杂度排一个排名&#xff0c;那么动态规划算法一定名列前茅。今天&#xff0c;我们通过由简单到困难的两道题目带大家学会动…

文字、图片免费生成视频和专属数字人,你不来试试吗?

查看生成的效果&#xff1a;AI产生的视频&#xff08;关注公众号&#xff0c;获取精彩内容&#xff09; 您是否想要制作一些令人惊叹的视频&#xff0c;但又没有视频编辑的技能或经验&#xff1f;您是否想要利用人工智能的力量&#xff0c;让您的图片和声音变成动态的视频&…

如何强制任何Android应用程序进入全屏沉浸式模式(无生根)

谷歌在2012年发布了Android版本的Chrome&#xff0c;并且从未费心给它一个全屏模式。如果您厌倦了等待自己喜欢的Android应用程序提供全屏&#xff0c;则可以使用沉浸式模式自行完成。 来吧&#xff0c;谷歌&#xff0c;我真的一直在乞求你多年&#xff01;没有理由不给我们一…

【Go语言反射reflect】

Go语言反射reflect 一、引入 先看官方Doc中Rob Pike给出的关于反射的定义&#xff1a; Reflection in computing is the ability of a program to examine its own structure, particularly through types; it’s a form of metaprogramming. It’s also a great source of …

C语言——深入理解指针(4)

目录 1.回调函数 2. qsort 函数的使用 2.1 排序整型数据 2.2 排序结构体数据 3. qsort 函数的模拟实现 1.回调函数 回调函数就是通过一个函数指针调用的函数。 你把函数的地址作为参数传递给另一个函数&#xff0c;当这个指针被用来调用其所指向的函数时&#xff0c;被调…

【web安全】ssrf漏洞的原理与使用

前言 菜某对ssrf漏洞的总结。 ssrf的作用 主要作用&#xff1a;访问外界无法访问的内网进行信息收集。 1.进行端口扫描&#xff0c;资源访问 2.指纹信息识别&#xff0c;访问相应的默认文件 3.利用漏洞或者和payload进一步运行其他程序 4.get类型漏洞利用&#xff0c;传参数…

用CHAT 写一份销售人员激励方案

问CHAT &#xff1a;写一份销售人员早会激励方案 CHAT回复&#xff1a; 标题&#xff1a;鼓舞斗志&#xff0c;迎接新的一天 -- 销售人员早会激励方案 一、会议的氛围设定&#xff1a; 深呼吸&#xff0c;准备开始一天的事业&#xff1a;清晨的阳光&#xff0c;温暖而明亮&…