数据结构与算法面试知识点汇总(超全)

文章目录哈希函数和哈希表哈希函数哈希表哈希函数和哈希表哈希函数哈希函数(散列函数):类似于函数调用一样,给一个字符串作为输入,返回一个哈希码性质:1)输入域是无穷大的2)输出域是有穷的,输出域要比输入域小3)哈希函数不是一个随机函数,只要输入一样,输出就一样4)输入域这么大而输出域这么小,而由于特性3的关系,就会导致输入不一样也有可能得到输出一样的值(哈希碰撞)5)虽然有哈希...
摘要由CSDN通过智能技术生成


一、哈希函数和哈希表

01 哈希函数

哈希函数(散列函数):类似于函数调用一样,给一个字符串作为输入,返回一个哈希码。
哈希表(散列表):把哈希码映射到表中的一个位置来访问记录,存放记录的数组就叫散列表

  • 直接寻址法:hash(key) = a * key + b
  • 数字分析法
  • 平方取中法
  • 折叠法
  • 随机数法
  • 除留余数法

性质:
1)输入域是无穷大的
2)输出域是有穷的,输出域要比输入域小
3)哈希函数不是一个随机函数,只要输入一样,输出就一样
4)输入域这么大而输出域这么小,而由于特性3的关系,就会导致输入不一样也有可能得到输出一样的值(哈希碰撞)
5)虽然有哈希碰撞,如果输入够多,将在整个输出域上均匀地出现返回值,不一定完全平分,但输出域中的每个哈希码一定被差不多个输入域中的值对应
6)因为要做到均匀分布,于是哈希函数跟输入的规律是没有关系的,可以打乱输入规律

推广:
当输入域足够大,经过一个哈希函数计算完后保证输出域是均匀分布时,
对输出域的每一个哈希码模%上一个m,最终的域会缩减成0至m-1,也均匀分布

工程应用:
如何找到1k个相对独立的哈希函数,一个哈希函数的规律跟另一个哈希函数的规律是无关的
只要用1个哈希函数就能改出1k个来,如:哈希f1 + k * 哈希f2 = 哈希fn

02 哈希表

实现原理:
在这里插入图片描述

解决哈希冲突(key1和key2称为同义词)的方法:

  • 开放寻址法(包括线性探测再散列、二次探测再散列(改善堆积现象)、伪随机探测再散列、双散列法),容易造成堆积现象
  • 链地址法:在原地址新建一个空间,以链表结点的形式插入到该空间。链地址算法的基本思想是将所有哈希地址为 i 的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。插入新数据项的时间随装载因子线性增长。

哈希表扩容:

如果发现某个位置上的链的长度到达k了,那么可以认为其它位置上链的长度也到达k了。此时,可能下一次加记录的话效率就不行了,这时就要经历哈希表的扩容

  • 首先要将原来的哈希域(如前面的17),扩展到104(或别的)。
  • 扩完之后如果要模104,则原来的记录都会失效。此时要将每一个记录都拿出来,用哈希函数计算完之后去模上104,再分配其在哪个位置。

那么,扩容代价不用计算吗?为什么能做到O(1)呢?
一方面样本量是N,若每次扩容都让哈希域长度增加k倍,则扩容平均代价是O(logkN)可很低
另一方面还可以离线扩容。
于是哈希表的增删改查是O(1)的。

Q:有一个大文件100T,每一行是一个字符串,是无序的,让你打印重复的字符串
A:如果单核单CPU单台机器去搞这个事情,那头都大了
Q:说得对,那你怎么办?
A:这是一个大数据问题,可以通过哈希来分流,你给我多少台机器呢?大文件存在哪呢?
Q:给你1000台机器干这个事情,大文件存在一个分布式文件系统上,按行读有很快的工具
A:

  • 1)首先对机器标号0-999,把每一行作为一个文本读出来,
    利用哈希函数去算一个哈希码,去模1000,
    当然也不一定要你提供的1k台机器,只要我能知道里面有m种字符串就好了,就模m
  • 2)然后将结果放到对应标号的机器中,用一个文件记录下来。
    这样就把这个大文件分到了m台机器上去,而且相同的重复文本一定会分配到同一台机器上(哈希函数的性质:相同输入相同输出,不同输入均匀分布)
  • 3)然后在单台机器上去统计有哪些重复的,最后就是1k台机器并发
    如果单台机器上的文件还是太大,再在这个机器上做相同的步骤,在单台机器上并行计算即可

二、布隆过滤器

主要用于解决爬虫去重问题以及黑名单问题,其短板是它有失误率。

这个失误是指:宁可错杀,绝不放过1个的类型。如果是坏人,一定报警。如果是好人,也可能报警,但几率较小

以黑名单问题来举例:

有这么个需求,100亿个URL是黑名单,假设每个URL是64字节,
当用户想搜索某个URL时,如果它是黑名单里的URL,就要对它进行过滤

解决方案:
1)首先它可以通过哈希表实现,但是需要多少个空间呢?

  • 由于这里面不用存value,只用存key,于是它是一个hashSet,key是URL字符串
  • 这里起码需要6400亿个字节才能把这些URL装下,还不算指针、索引等所占的空间,实际上是比6400亿字节(640G)大的, 如果做成哈希表全放内存中,代价是很高的,而且服务器还要用分布式内存
  • 这个设计就耗费了巨大的代价。即便用哈希函数分流到多个机器上,这浪费了机器

2)那怎么能仅适用一台机器就能提供服务呢?——布隆过滤器

  • 在哈希来哈希去的经典方法说了以后,应该问允许有很低的失误率吗?——允许。这个时候就可以使用布隆过滤器。它是一个bit类型的map。

实现方法:
第一步:
准备一个数组,从0到m-1位置,每个位置上不是整数也不是串,是一个bit,只有两种状态0/1
int *arr = new int[1000]; // 分配4*1000字节的连续内存,共4*1000*8个bit
在这里插入图片描述
这样,就用基础类型int做出了一个0到m-1长度为m的bit类型的数组。如果想省空间,就用long类型,8个字节64位。此外还能做成矩阵的形式。
如果想让第300000个bit位置置1,此时int index = 300000,

  • ① 定位这个bit来自于哪个桶 int intIndex = index / 32;
  • ② 定位这个位置来自于这个桶的哪个bit位 int bitIndex = index % 32;
  • arr[intIndex] = (arr[intIndex] | (1 << bitIndex));

第二步:
有了这个数组以后,

  • ① 将黑名单内的所有URL,输入k个相互独立的哈希函数中得到k个哈希码分别取模上m,算出来的对应数组中的那个位置1。若产生哈希碰撞,不管,还是置1。这个过程结束以后,URL进入布隆过滤器。注意:这个数组应当长一些,如果太短,则很容易每个位置都变成1,这样用户输入的URL就全都是黑名单了。
  • ② 查URL的过程:将用户提供的URL,也做步骤①,如果算出来的k个位置都已经置1,则说这个URL在黑名单中。如果仅有一个甚至几个没被置1,说明这个URL不在黑名单中(哈希函数特性:相同输入一定有相同输出)。失误的时候就出现在数组开的比较小。
  • 通过调整数组的大小m和哈希函数的个数k,可以得到不同的失误率。
    在这里插入图片描述

三、一致性哈希

经典服务器抗压结构:
比如提供一个根据用户QQ号(key)返回其网龄(value)的服务。如何使其负载均衡呢?

  • 首先会有一个前端(Nginx),用于接受请求,请求可以发送到任意一个前端上,提供的服务相同
  • 然后有一个后端集群组,假设有3台机器m1,m2,m3
  • 首先信息会发送到前端上,经前端的同样一份哈希函数算出哈希码,模上3得到对应的机器,将这个信息存到对应的机器中。如果数据足够多,数据是均匀地存在三台机器中的,负载均衡:每台机器处理的东西是差不多的,CPU占用差不多,内存使用差不多,相关系统性能指标差不多。
  • 在查的时候,依然是经过上面的步骤,由于相同输入一定导致相同输出,就会到相应的机器上去拿出这个记录,返回给前端,然后前端返回给用户。

上面提到的经典服务器有什么问题呢?——当增加机器或减少机器时,它就炸了

原因:
这和哈希表扩容是一样的,如果加机器加到100,原来模的是3,现在要模100,所有的数据归属全变了。所以必须要把原来的记录都拿出来再算一次,然后迁移到相应的机器上。代价很高

于是有了一致性哈希结构,可实现把原来全部数据迁移的代价变得很低,并负载均衡

具体是这样的:
在这里插入图片描述
那么如何实现它呢?

  • 对于后端——还是那三台机器
  • 对于前端——还是无差别的负载的服务器

然后,对三台机器对应的哈希值排好序之后做成一个数组存到前端中去
这样,不管请求发送到前端的哪一个机器上,由于每个机器上都有这么一个数组,二分的方式找到第一个大于等于算出来的哈希值的那个位置,就知道这个请求应当由哪个机器处理了。
即便你有10亿台机器,由于采用二分的方法,log以后复杂度也是很低的。

那为什么说它克服了传统服务器结构中加减机器的问题呢?

比如加了一台m4,同样也映射到环上。
在这里插入图片描述
这样,

  • 在增加机器时,不需要去迁移m1和m3全部的数据,只用迁移原本属于m2的数据即可完成
  • 在减少机器时,只需要把m4的数据迁移到m3上即可

于是,这种结构,加减机器数据迁移的代价是非常低的

但是,存在什么问题呢

  1. 在机器数量比较少时,这个环是不能够保证其均分的(哈希函数的性质:量在多的时候,才是均匀分布的)。这样负载就会出现问题,一台机器很忙,一台机器却闲得发慌
  2. 即便可以保证均匀,在加一个机器之后负载就不均了

把上述两个问题解决了,一致性哈希就能既做到数据迁移的代价小,又能保证负载均衡

虚拟结点技术:

  • 真实机器m1,给它分配1k个虚拟结点
  • m2,m3也同样都分配1k个虚拟结点

此时,不让m1,m2,m3各自的ip去映射到环上,

  • 准备一张路由表,这个路由表可以由真实机器去查它
  • 8
    点赞
  • 212
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构与算法是计算机科学中非常重要的知识领域,它们对于开发高效和优化的软件系统至关重要。下面是一些数据结构与算法的重要知识点: 1. 数组(Array):一个有序元素集合,可以通过索引来访问其中的元素。 2. 链表(Linked List):由节点组成的线性数据结构,每个节点包含一个值和指向下一个节点的指针。 3. 栈(Stack):一种遵循后进先出(LIFO)原则的数据结构,只允许在栈顶进行插入和删除操作。 4. 队列(Queue):一种遵循先进先出(FIFO)原则的数据结构,允许在队尾插入元素,在队头删除元素。 5. 哈希表(Hash Table):根据键(key)直接访问值(value)的数据结构,通过散列函数将键映射到特定的存储位置。 6. 树(Tree):一种非线性数据结构,由节点和边组成,每个节点可以有多个子节点。 7. 图(Graph):由节点和边组成的非线性数据结构,节点之间可以相互连接形成任意的关系。 8. 排序算法(Sorting Algorithm):用于将一组数据按照特定的顺序进行排列的算法,如冒泡排序、快速排序等。 9. 查找算法(Searching Algorithm):用于在一组数据中查找指定元素的算法,如线性查找、二分查找等。 10. 算法复杂度(Algorithm Complexity):用来衡量算法执行效率的指标,包括时间复杂度和空间复杂度。 这些只是数据结构与算法中的一部分知识点,深入学习和理解这些知识将有助于你开发更高效的软件系统。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值