zoukankan      html  css  js  c++  java
  • Python美女[从新手到高手]--阅读"见个面问题 HashMap 储存方法"联想

    伯乐在线 上看到一篇文章一道面试题看 HashMap 的存储方式。也就是问:


    在 HashMap 中存放的一系列键值对,当中键为某个我们自己定义的类型。放入 HashMap 后,我们在外部把某一个 key 的属性进行更改,然后我们再用这个 key 从 HashMap 里取出元素。这时候 HashMap 会返回什么?


    怎样面试者直接答“这要看自己定义类型的hash值了”,我想面试官会非常惬意。

    联想到python中dict的实现,python中字典一般不存在这个问题,由于key的hash值默认是id值,一个对象的id是固定的。

    看例如以下代码:


    我们能够通过__hash__改动默认hash值。所以__hash__方法还是要看详细业务逻辑,比方我们业务任务name值一样就是同一个对象,看例如以下代码:

    class B:
    	def __init__(self,name):
    		self.name=name
    	def __hash__(self):
    		return hash(self.name)
    d={}
    b1=B('skycrab')
    b2=B('skycrab1')
    d[b1]=1
    d[b2]=2
    b2.name='skycrab'
    d[b2]=3
    print d
    我信心满满的觉得name为‘skycrab‘的值会被更新为3,可事实例如以下:

    {<__main__.B instance at 0x02543210>: 2, <__main__.B instance at 0x02543210>: 3, <__main__.B instance at 0x025436C0>: 1}

    这让我百思不得其解,hash值明明一样,为什么会觉得是不同对象。导致添加了一个。突然灵光一闪。难道key也须要做比較?打开python源代码我们看看lookdict函数,

    当更新字典时会去寻找合适的hashtable位置。调用的就是lookdict函数。

    static dictentry *
    lookdict(dictobject *mp, PyObject *key, register long hash)
    {
    	register size_t i;
    	register size_t perturb;
    	register dictentry *freeslot;
    	register size_t mask = (size_t)mp->ma_mask;
    	dictentry *ep0 = mp->ma_table;
    	register dictentry *ep;
    	register int cmp;
    	PyObject *startkey;
    
    	i = (size_t)hash & mask;
    	ep = &ep0[i];
    	if (ep->me_key == NULL || ep->me_key == key)
    		return ep;
    
    	if (ep->me_key == dummy)
    		freeslot = ep;
    	else {
    		if (ep->me_hash == hash) {
    			startkey = ep->me_key;
    			cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); //比較key的值
    			if (cmp < 0)
    				return NULL;
    			if (ep0 == mp->ma_table && ep->me_key == startkey) {
    				if (cmp > 0) //仅仅有key相等才会返回已有的位置,否则会寻找一个新的位置
    					return ep;
    			}
    			else {
    				/* The compare did major nasty stuff to the
    				 * dict:  start over.
    				 * XXX A clever adversary could prevent this
    				 * XXX from terminating.
     				 */
     				return lookdict(mp, key, hash);
     			}
    		}
    		freeslot = NULL;
    	}

    上面是lookdict的部分源代码(最后没有大括号),如上代码凝视,原来仅仅有hash值一样且key值相等才有更新。那么这就好办了,定义__eq__方法就可以:

    class B:
    	def __init__(self,name):
    		self.name=name
    	def __hash__(self):
    		return hash(self.name)
    	def __eq__(self,r):
    		if self.name == r.name:
    			return True
    		else:
    			return False
    d={}
    b1=B('skycrab')
    b2=B('skycrab1')
    d[b1]=1
    d[b2]=2
    b2.name='skycrab'
    d[b2]=3
    print d
    
    这下结果最终符合期望了,{<__main__.B instance at 0x025F2620>: 2, <__main__.B instance at 0x025F25F8>: 3}


    这里我们扩展一下,python中的dict默认採用hash_map的存储结构,所以查找效率非常高。但hash_map的查找效率不稳定。

    hash_map的时间复杂度在O(1)-O(N),而基于树结构的map时间复杂度O(logN),比較稳定。

    所以在C++中使用hash_map还是map是有考究的。详细能够看看【C++对话系列-产生真正的hash对象】一个。



    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    剑指offer-重建二叉树
    Java集合学习-总体框架
    leetcode-6-ZigZag Conversion
    海拔高度与大气密度的关系函数
    C++ 获取文件夹下的所有文件名
    01-复杂度1. 最大子列和问题
    00-自测5. Shuffling Machine
    00-自测4. Have Fun with Numbers
    00-自测3. 数组元素循环右移问题
    00-自测2. 素数对猜想
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4851542.html
Copyright © 2011-2022 走看看