哈希函数和哈希表

哈希函数:

1.经典的哈希函数接受的输入域是无穷的,而输出域相对来说是有限的。比如MD5会返回一个哈希码它是16进制的,是2^64次方的范围;SHA-1会返回2^128的范围。不管什么范围,返回域是有限的。

2.哈希函数不是随机函数,相同的输入值一定得到相同的返回值。

3.由于哈希函数接受的输入域是无穷的,而输出域相对来说是有限的,可能会导致不同的输入也可能对应一个输出。把这种情况称为哈希碰撞。

4.(离散型!重要)虽然输入域是无穷大,输出域是一个s,但是不同的输入如果想得到s域上的返回值,在整个s域上得到的返回值会均匀分布。例如:input(0-99),output(0,1,2),如果输入域的数都不相同,正好出现了100个数,那么输出得到0的次数差不多是33次,得到1的次数差不多也是33次,得到2的次数差不多也是33次。当100个数都不同,得到的每一个数的次数基本维持在平均水平。(均衡性越好,则离散型越好,这个哈希函数越优良)哈希函数这种离散型是为了打乱输入规律。

哈希表:

假设一开始一个数组长度为17,假设要把一个东西放到这个哈希表中去,假设要放一个字符串“zuo”,“zuo”通过哈希函数算出来的哈希值是2^64范围的,将这个值mod17,最终会放到0-16位置中的一个。哈希表一开始就是一坨哈希域0到16,0到16每一个后面串上链表。当加的不同的数据没那么多时,因为哈希函数在0-16上算出来的返回值是均匀分布的,则0的链表长度,1的链表长度...一直到16的链表长度,几乎均匀的长。

java里面是怎么实现的呢?开头还是这么算,0到16就相当于桶,每一个桶后面串的是红黑树。在java里面会遇到hashset和hashmap,hashset往里加的时候只能加个key,而hashmap往里加的时候可以放(key,value)。这个hashset和hashmap在结构上其实是一样的,hashmap再多封装一个数据对象就可以了。比如0这个位置后面串着"zuo"->"cheng",用hashset可以查"zuo"在结构中有没有,用hashmap可以在0这个位置找到“zuo”再把对应的数据对象取出来。

(注意:hash函数不是排序的,跟排序一毛钱关系都没有,hash函数在java里面叫做hashmap,红黑树在java里面叫做treemap)

hashmap在面试时候如果要用它,可以认为增删改查都是O(1)的操作。但虽然可以默认为常用时间,但是常数项较大。和加入的数据量没关系,但是常数项比较大,因为hash函数在算的时候代价比较高。


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

public class HashMap {

	public static void main(String[] args) {
		HashMap<String, String> map = new HashMap<>();
		map.put("zuo", "31");

		System.out.println(map.containsKey("zuo"));
		System.out.println(map.containsKey("chengyun"));
		System.out.println("=========================");

		System.out.println(map.get("zuo"));
		System.out.println(map.get("chengyun"));
		System.out.println("=========================");

		System.out.println(map.isEmpty());
		System.out.println(map.size());
		System.out.println("=========================");

		System.out.println(map.remove("zuo"));
		System.out.println(map.containsKey("zuo"));
		System.out.println(map.get("zuo"));
		System.out.println(map.isEmpty());
		System.out.println(map.size());
		System.out.println("=========================");

		map.put("zuo", "31");
		System.out.println(map.get("zuo"));
		map.put("zuo", "32");
		System.out.println(map.get("zuo"));
		System.out.println("=========================");

		map.put("zuo", "31");
		map.put("cheng", "32");
		map.put("yun", "33");

		for (String key : map.keySet()) {
			System.out.println(key);
		}
		System.out.println("=========================");

		for (String values : map.values()) {
			System.out.println(values);
		}
		System.out.println("=========================");

		map.clear();
		map.put("A", "1");
		map.put("B", "2");
		map.put("C", "3");
		map.put("D", "1");
		map.put("E", "2");
		map.put("F", "3");
		map.put("G", "1");
		map.put("H", "2");
		map.put("I", "3");
		for (Entry<String, String> entry : map.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue();
			System.out.println(key + "," + value);
		}
		System.out.println("=========================");

		// you can not remove item in map when you use the iterator of map
//		 for(Entry<String,String> entry : map.entrySet()){
//			 if(!entry.getValue().equals("1")){
//				 map.remove(entry.getKey());
//			 }
//		 }

		// if you want to remove items, collect them first, then remove them by
		// this way.
		List<String> removeKeys = new ArrayList<String>();
		for (Entry<String, String> entry : map.entrySet()) {
			if (!entry.getValue().equals("1")) {
				removeKeys.add(entry.getKey());
			}
		}
		for (String removeKey : removeKeys) {
			map.remove(removeKey);
		}
		for (Entry<String, String> entry : map.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue();
			System.out.println(key + "," + value);
		}
		System.out.println("=========================");

	}

}

题目:

设计一种结构,在该结构中有如下三个功能:

insert(key):将某个key加入到该结构,做到不重复加入。

delete(key):将原本在结构中的某个key移除。

getRandom():等概率随即返回结构中的任何一个key。

要求:insert,delete和getRandom方法的时间复杂度都是O(1)


import java.util.HashMap;

public class RandomPool {

	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;
		}

		public void insert(K key) {
			if (!this.keyIndexMap.containsKey(key)) {
				this.keyIndexMap.put(key, this.size);
				this.indexKeyMap.put(this.size++, key);
			}
		}

		public void delete(K key) {
			if (this.keyIndexMap.containsKey(key)) {
				int deleteIndex = this.keyIndexMap.get(key);
				int lastIndex = --this.size;
				K lastKey = this.indexKeyMap.get(lastIndex);
				this.keyIndexMap.put(lastKey, deleteIndex);
				this.indexKeyMap.put(deleteIndex, lastKey);
				this.keyIndexMap.remove(key);
				this.indexKeyMap.remove(lastIndex);
			}
		}

		public K getRandom() {
			if (this.size == 0) {
				return null;
			}
			int randomIndex = (int) (Math.random() * this.size); // 0 ~ size -1
			return this.indexKeyMap.get(randomIndex);
		}

	}

	public static void main(String[] args) {
		Pool<String> pool = new Pool<String>();
		pool.insert("zuo");
		pool.insert("cheng");
		pool.insert("yun");
		System.out.println(pool.getRandom());
		System.out.println(pool.getRandom());
		System.out.println(pool.getRandom());
		System.out.println(pool.getRandom());
		System.out.println(pool.getRandom());
		System.out.println(pool.getRandom());

	}

}

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值