左神算法5:哈希函数、哈希表、布隆过滤器、一致性哈希、并查集问题和岛结构

1.哈希函数和哈希表

1.1哈希函数特点

  • 经典的哈希函数的输入域是无穷大的
  • 哈希函数的输出域是有穷尽的,虽然很大,但是是个固定的数值
  • 当输入参数固定的情况下,得到的输出参数固定,它不是随机函数,样本固定得到的输出值固定
  • 输入不一样,也有可能得到相同的哈希值(哈希碰撞)
  • 虽然会有两个输入对应同一个输出,但是对于大量的输入对应的输出域基本是平均分的即S域上均匀分布。

推论:对于输入对应的哈希值,使其模M之后的结果同样是均匀分布的。
----------例题:如何做出1000个哈希函数,且互不相关
哈希码中每个位置针对于其他位置均独立,所以可以采用高位和低位拆分开形成两个哈希函数,之后再将H+N*L形成新的哈希函数,需要多少只需要修改N的范围就可以。

  • 大数据题目:假设有一个大文件,大文件中每一行都是一个字符串,需要将大文件中所有重复的字符串打印出来。(100T的大文件)
    此时就是通过哈希分流,此时询问分配机器的数目,将机器标号,然后利用分布式文件读取的形式将所有的文件读取出来,利用哈希函数将每一行算出哈希code,然后再模数目,此时就会将重复的文本放置到了同一台机器上。(相同的输入一定对应相同的输出)文件种类会均匀分布到每台机器上,此时可以同时跑并行任务划分出更小的文件形式,再去判断。

1.2哈希表

数组+链表
数组+红黑树

设计RandomPool结构

【题目】 设计一种结构,在该结构中有如下三个功能:
insert(key):将某个key加入到该结构,做到不重复加入。
delete(key):将原本在结构中的某个key移除。
getRandom(): 等概率随机返回结构中的任何一个key。
【要求】 Insert、delete和getRandom方法的时间复杂度都是 O(1)

  • 要求等概率解决思路:建立两个哈希表,只有add和random的时候,第一个表中用于存放数据,同时记录size大小,第二个表中存放size和对应的key,对于random返回的时候,则再size中随机抽出一个,然后在第二个表中返回key。
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);
	}
}

2.布隆过滤器

假设有一百亿个url的黑名单,在搜索这些黑名单的时候不想显示,每个url是64个字节,在用户搜索的时候就需要先过滤。

布隆过滤器存在失误率:如果一个rul确实属于黑名单上,确实不会误报,但是可能存在某个url不在黑名单上但是可能会返回在黑名单上这种可能。

大致流程:设置一个很长的数组,设置多个哈希函数,url经过哈希函数,使得对应的哈希值描黑,将多个url都放入到数组之中,对应的哈希值全部描黑。之后读取url的时候,如果经过哈希函数对应的哈希值全部都是黑的,则认为属于黑名单。

3.一致性哈希

一致性哈希主要就是解决当机器减少或增加的时候,大面积的数据重新哈希的问题,主要从下面 2 个方向去考虑的,当节点宕机时,数据记录会被定位到下一个节点上,当新增节点的时候 ,相关区间内的数据记录就需要重新哈希。

4.并查集问题

并查集有两个功能:

  • 判断两个集合中的元素是否在同一个集合中,
  • 根据两个集合中的元素,将不是同一集合的整和到一起。最开始需要将所有的样本点给出。

步骤:

  • 1.首先第一步将所有各自的数目整成一个集合(每个节点单独形成一个集合)
  • 2.在一个集合中每个节点都是自己集合中的代表节点,当集合合并的时候,如果决定将2挂到1的底下,此时2所在集合中,只有1节点指向自己,所以1为这个集合的代表节点。
  • 3.由于每一个集合中都有一个集合的代表节点,所以可以通过代表节点来进行是否在同一个集合的判断,也可以将另外一个集合的代表节点连接到本集合中,此时整个集合也只有一个代表节点,实现集合的整合。(首先进行元素数目的判断,将少元素的集合挂到多元素的底下)
  • 优化:在向上查找的过程中,将路径上的所有元素直接连接到代表节点上。
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);
			}
		}
	}
}

5.岛问题

一个矩阵中只有0和1两种值,每个位置都可以和自己的上、下、左、右

  • 四个位置相连,如果有一片1连在一起,这个部分叫做一个岛,求一个矩阵中有多少个岛?
  • 举例:
    0 0 1 0 1 0
    1 1 1 0 1 0
    1 0 0 1 0 0
    0 0 0 0 0 0
    这个矩阵中有三个岛。
	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);
}

分析:
如果矩阵较大,采用分块方法,最后将分块进行合并,将岛问题转换成为并查集的题目。

  • 将岛的数目确定中心,同时只关注边界上的岛中心。
  • 合并时计算岛两个本相连是否属于一个集合(块),如果不属于则进行集合的合并。

你可能感兴趣的:(左神算法)