推论:对于输入对应的哈希值,使其模M之后的结果同样是均匀分布的。
----------例题:如何做出1000个哈希函数,且互不相关
哈希码中每个位置针对于其他位置均独立,所以可以采用高位和低位拆分开形成两个哈希函数,之后再将H+N*L形成新的哈希函数,需要多少只需要修改N的范围就可以。
数组+链表
数组+红黑树
【题目】 设计一种结构,在该结构中有如下三个功能:
insert(key):将某个key加入到该结构,做到不重复加入。
delete(key):将原本在结构中的某个key移除。
getRandom(): 等概率随机返回结构中的任何一个key。
【要求】 Insert、delete和getRandom方法的时间复杂度都是 O(1)
public static class Pool<k>{
private HashMap<K,Integer> keyIndexMap;
private HashMap<Integer,K> indexKeyMap;
private int size;
public Pool(){
this.keyIndexMap = new HashMap<K,Integer>();
this.indexKeyMap = new HashMap<Integer,K>();
this.size = 0;
}
//将不重复的key放到hash表中非常简单,只需要简单判断目前不存在key,然后放入key和size就可以
public void insert(K key){
if(!this.keyIndexMap.containsKey(key)){
this.keyIndexMap.put(key,this.size);
this.indexKeyMap.put(this.size++,key);
}
}
//删除key,将需要删除的key和最后一个key进行调换,然后删除最后一条数据,保证整个区间不会出现洞。
public void delete(K key){
//先判断是否包含所要删除的key
if(this.keyIndexMap.containsKey(key)){
//将key对应的size值读取出来,赋值给deleteIndex
int deleteIndex = this.keyIndexMap.get(key);
//将最后的size读取出来
int lastIndex = --this.size;
//将最后一个key读取出来
K lastKey = this.indexKeyMap.get(lastIndex);
this.keyIndexMap.put(lastKey,deleteIndex);
this.indexKeyMap.put(deleteIndex,lastKey);
this.keyIndexMap.remove(key);
this.indexKeyMap.remove(lastIndex);
}
}
//利用size长度进行等概率获取任意key
public K getRandom(){
if(this.size== 0){
return null;
}
int randomIndex = (int) (Math.random()*this.size);
return this.indexKeyMap.get(randomIndex);
}
}
假设有一百亿个url的黑名单,在搜索这些黑名单的时候不想显示,每个url是64个字节,在用户搜索的时候就需要先过滤。
布隆过滤器存在失误率:如果一个rul确实属于黑名单上,确实不会误报,但是可能存在某个url不在黑名单上但是可能会返回在黑名单上这种可能。
大致流程:设置一个很长的数组,设置多个哈希函数,url经过哈希函数,使得对应的哈希值描黑,将多个url都放入到数组之中,对应的哈希值全部描黑。之后读取url的时候,如果经过哈希函数对应的哈希值全部都是黑的,则认为属于黑名单。
一致性哈希主要就是解决当机器减少或增加的时候,大面积的数据重新哈希的问题,主要从下面 2 个方向去考虑的,当节点宕机时,数据记录会被定位到下一个节点上,当新增节点的时候 ,相关区间内的数据记录就需要重新哈希。
并查集有两个功能:
步骤:
public static class UnionFindSet{
public HashMap<Node,Node> fatherMap;//第一个Node代表子节点,后面代表父节点
public HashMap<Node,Integer> sizeMap;//Node所在集合中节点的数目
public UnionFindSet(){
fatherMap = new HashMap<Node,Node>();
sizeMap = new HashMap<Node,Integer>();
}
//初始化操作
public void makeSets(List<Node> nodes){
fatherMap.clear();
sizeMap.clear();
for(Node node:nodes){
fatherMap.put(node,node);
sizeMap.put(node,1);
}
}
private Node findHead(Node node){
Node father = fatherMap.get(node);
if(father != node){
father = findHead(father);
}
fatherMap.put(node,father);
return father;
}
public boolean isSameSet(Node a,Node b){
return findHead(a) == findHead(b);
}
public void union(Node a,Node b){
if(a == null || b = null){
return;
}
Node aHead = findHead(a);
Node bHead = findHead(b);
if(aHead != bHead){
int aSetSize = sizeMap.get(aHead);
int bSetSize = sizeMap.get(bHead);
//进行判断,如果长度不同,则将短的集合连接到长集合的上面,同时将整体集合的长度相加作为新集合的总长度
if(aSetSize <= bSetSize){
fatherMap.put(aHead,bHead);
sizeMap.put(bHead,aSetSize +bSetSize);
}else{
fatherMap.put(bHead,aHead);
sizeMap.put(aHead,aSetSize+bSetSize);
}
}
}
}
一个矩阵中只有0和1两种值,每个位置都可以和自己的上、下、左、右
public static int countIslands(Int[][] m){
if(m == null || m[0] == null){
return 0;
}
int N = m.length;
int M = m[0].length;
int res = 0;
for(int i = 0;i<N;i++){
for(int j = 0;j<M;j++{
if(m[i][j] == 1){
res++;
infect(m,i,j,N,M);
}
}
}
return res;
}
public static void infect(int[][]m,int i,int j,int N,int M){
if(i<0 || i>=N||j<0||j>=M || m[i][j] != 1){
return ;
}
m[i][j] =2;
infect(m,i+1,j,N,M);
infect(m,i,j+1,N,M);
infect(m,i-1,j,N,M);
infect(m,i,j-1,N,M);
}
分析:
如果矩阵较大,采用分块方法,最后将分块进行合并,将岛问题转换成为并查集的题目。