python中的字典(即HashMap结构)+代码实现

python里的字典就像java里的HashMap,以键值对的方式存在并操作,其特点如下

  • 通过键来存取,而非偏移量;
  • 键值对是无序的;
  • 键和值可以是任意对象;
  • 长度可变,任意嵌套;
  • 在字典里,不能再有序列操作,虽然字典在某些方面与列表类似,但不要把列表套在字典上

目录

  • 一、结构
  • 二、python库函数简单实现(必读)
    • 1.collections.defaultdict()
    • 2.dict()
    • 3.直接Dict = {} ,略
    • 4.Counter
    • 5.补充Counter(例子)
    • 6.字典的操作
  • 三、python自定义实现(选读)
    • 1.确定链表
    • 2.get、put

一、结构

1.和邻接表的结构一样,一个定长的数组,这个数组的每一个元素都是一个链表的头结点,headers是一个定长的数组,数组当中的每一个元素都是一个链表的头结点。也就是说根据这个头结点,我们可以遍历这个链表。数组是定长的,但是链表是变长的,所以如果我们发生元素的增删改查,本质上都是通过链表来实现的,总之:它本质上就是一个元素是链表的数组。

2.HashMap中每个节点的存储方式为形式, 其中,key 无序不可重复,value可以重复

3.当hash值不同时,对象一定不同。当hash值相同时,对象可能相同,也可能不同

python中的字典(即HashMap结构)+代码实现_第1张图片
4.可能大体上看了,具体数据结构里面是可以把哈希表看成一个二维数组,哈希表中的每一个元素又存储了哈希值(hash)、键(key)、值(value)3个元素。

enteies = [
    ['--', '--', '--'],
    [hash, key, value],
    ['--', '--', '--'],
    ['--', '--', '--'],
    [hash, key, value],
]

由上可见哈希表的存储结构,我们也可以看出,元素之间有一些空元素,我们通过增加一个元素来讲解具体实现。

  • 计算key的hash值【hash(key)】,在和mask做与操作【mask=字典的最小长度(DictMinSize)-1】,运算后会得到一个数字index,这个index就是要插入enteies哈希表中的下标位置。
  • 若index下标位置已经被占用,则会判断enteies的key是否与要插入的key相等。
  • 如果key已经存在,则更新vlaue值
  • 如果key不存在,就表示hash冲突,会继续向下寻找空位置

二、python库函数简单实现(必读)

1.collections.defaultdict()

# 加入输入是一个s=jhkdfhii,统计次数
from collections import defaultdict
Defdict = defaultdict(int)
for i in range(len(list(s))):
	Defdict[s[i]] += 1 # 对于value的自加,也可以赋值(比如value是下标i)


2.dict()

records = dict() # 初始化

# 这里用的dict()注意dict里面是空要先判断在不在里面赋值为1,defaultdict不用判断可以直接+1

for j in range(len(list(s))):
	if s[j] not in records # 判断是否含有元素
		record[s[j]] = idx #一样的可以赋值或者自加
return record # 返回字典的key,value

defaultdict和dict和Counter区别

  • 当定义一个字典没有相应的key值时,defauldict()会在字典中添加这个key值并赋值为0(当字典里的key不存在但被查找时,返回的不是keyError而是一个默认值,)
  • 而直接使用dict()来定义则会报错:找不到相应的key值(当字典里的key不存在但被查找时,返回的是keyError)
  • 但使用if语句来主动为key赋值,也能达到defaultdict()一样的效果。
  • Counter和defaultdict比较, Counter 的问题就是其数值必须是整数,本身就是用于统计数量,因此如果我们需要的数值是字符串,列表或者元组,那么就不能继续用它。
# * 传入的是列表s
s = [('color', 'blue'), ('color', 'orange'), ('color', 'yellow'), ('fruit', 'banana'), ('fruit', 'orange'),
     ('fruit', 'banana')]
d = defaultdict(list)
for k, v in s:
    d[k].append(v)
print(d)  
# 输出结果:defaultdict(, {'color': ['blue', 'orange', 'yellow'], 'fruit': ['banana', 'orange', 'banana']})

# * 传入的是集合set
s = [('color', 'blue'), ('color', 'orange'), ('color', 'yellow'), ('fruit', 'banana'), ('fruit', 'orange'),
     ('fruit', 'banana')]
d = defaultdict(set)
for k, v in s:
    d[k].add(v)
print(d)
# 输出结果:defaultdict(, {'color': {'blue', 'yellow', 'orange'}, 'fruit': {'banana', 'orange'}})
# 这里需要注意的就是列表和集合的添加元素方法不相同,列表是list.append(),而集合是set.add()。

3.直接Dict = {} ,略

4.Counter

# 对于 Counter ,还可以通过关键字来初始化:
c = Counter(cats=4, dogs=8)
print(c)
# 输出:
Counter({'dogs': 8, 'cats': 4})

# Counter 的一些方法,除了上述介绍的most_common()外,还有:
# elements():返回一个迭代器,将所有出现元素按照其次数来重复 n 个,并且返回任意顺序,但如果该元素统计的次数少于 1 ,则会忽略,例子如下:
c = Counter(a=4, b=2, c=0, d=-2)
sorted(c.elements())
# ['a', 'a', 'a', 'a', 'b', 'b']
# subtract():减法操作,输入输出可以是 0 或者负数
c = Counter(a=4, b=2, c=0, d=-2)
d = Counter(a=1, b=2, c=3, d=4)
c.subtract(d)
print(c)
# Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})


# 此外,还有以下这些方法:
# 求和
sum(c.values())                
# 清空 Counter
c.clear()                      
# 转换为 列表
list(c)                         
# 转换为 集合
set(c)                          
# 转换为 字典
dict(c)                        
# 键值对
c.items()                       
Counter(dict(list_of_pairs))    
# 输出 n 个最少次数的元素
c.most_common()[:-n-1:-1]       
# 返回非零正数
+c      
# 返回负数
-c

# 此外,也可以采用运算符+,-,&,|,各有各不同的实现作用:
c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
# 加法操作 c[x] + d[x]
print(c + d)    # Counter({'a': 4, 'b': 3})                 
# 减法,仅保留正数
print(c - d )   # Counter({'a': 2})                 
# 交集:  min(c[x], d[x]) 
print(c & d)    # Counter({'a': 1, 'b': 1})             
# 并集:  max(c[x], d[x])
print(c | d)    # Counter({'a': 3, 'b': 2})

5.补充Counter(例子)

# 例如统计下面一句话的单词个数
text = "I need to count the number of word occurrences in a piece of text. How could I do that? " \
       "Python provides us with multiple ways to do the same thing. But only one way I find beautiful."
# 方法一:defaultdict
from collections import defaultdict
word_count_dict = defaultdict(int)
for w in text.split(" "):
    word_count_dict[w] += 1
    
# 方法二:Counter(或者直接 word_counter = Counter(text.split(" ")))
from collections import Counter
word_count_dict = Counter()
for w in text.split(" "):
    word_count_dict[w] += 1
print('most common word: ', word_count_dict.most_common(10))
# 补充Counter:Counter 其实就是一个计数器,它本身就应用于统计给定的变量对象的次数,因此,我们还可以获取出现次数最多的单词:

# Counter其他的一些应用例子:
# Count Characters
print(Counter('abccccccddddd'))  
# Count List elements
print(Counter([1, 2, 3, 4, 5, 1, 2]))  
# 输出结果:
Counter({'c': 6, 'd': 5, 'a': 1, 'b': 1})
Counter({1: 2, 2: 2, 3: 1, 4: 1, 5: 1})

# 方法三:{}
word_count_dict = {}
for w in text.split(" "):
    if w in word_count_dict:
        word_count_dict[w] += 1
    else:
        word_count_dict[w] = 1

6.字典的操作

# 1.获得字典dict中value的最小值所对应的键的方法
Min = min(num_dict, key=num_dict.get) 

# 2.翻转,这个可以根据value查找key
my_dic = {k:v for v,k in Defdict.items()} # 第一种
my_dic = dict(zip(Defdict.values(),Defdict.keys())) # 第二种
# 第三种
my_dic = {}
for k,v in Defdict.items():
    my_dic[v] = k

# 3.根据值查找键
# 遍历
my_dict ={"John":1, "Michael":2, "Shawn":3}
def get_key(val):
    for key, value in my_dict.items():
         if val == value:
             return key
    return "There is no such Key"
print(get_key(1)) 
print(get_key(2))
# 如果是查找值最小的键呢,先由1得到Min
print(get_key(Min)) 

# 3.根据值查找键
my_dict ={"John":3, "Michael":3, "Shawn":2}
list_of_key = list(my_dict.keys()) # 查找所有的键
list_of_value = list(my_dict.values()) # 查找所有的值
position = list_of_value.index(3) # value=3的
the_Key = list_of_key[position] # 输出的是value=3对应的第一个John

# 注意!!!防止值映射为下标
position2 = list_of_value[1] # 在[3,3,2]取出下标1,也就是第二个value=3,
the_Key = list_of_key[position2] # 输出的是key对应的['John','Michael','Shawn']下标为3的,没有就会报错


# 4.根据键查询值
dictt = {'name': 'Tom', 'age': 18, 'love': 'python'}
the_value = dictt ['key'] # 4.1直接使用键查找
# 4.2或者利用get函数使用键查找值
the_value = dictt .get('age') # 如果key不存在返回None
the_value = dictt .get('age','默认值') # 如果键不存在,设置返回默认值
# 4.3setdefault和get函数差不多,但是如果键不存在,则查找的内容当做键放入字典,并设置默认值,不设置为None
the_value = dictt.setdefault('age2')

# 5.查询所有键和值和键值对
the_keys = dictt.keys()
the_values = dictt.values()
the_items = dictt.items()
# 注意:返回的数据结构有三种不同的数据类型:dict_keys(),dict_values(),dict_items(),这样的数据是没有办法按照列表下标(例如the_values[1])进行访问的。
# 可以先转换列表
the_values = list(the_values)

# 6.has_key()判断给定的键是否存在于字典中,返回True 或者 False
dict = {'Name': 'Maxsu', 'Age': 7}
print ("Value : %s" %  dict.has_key('Age')) # Value:True


三、python自定义实现(选读)

参考自:https://blog.csdn.net/chinesehuazhou2/article/details/108786521

1.确定链表

要用这个hash值来决定这个节点应该存放在哪一条链表当中。只要hash函数确定了,只要值不变,计算得到的hash值也不会变。所以我们查询的时候也可以遵循这个逻辑,找到key对应的hash值以及对应的链表。

在Python当中由于系统提供了hash函数,所以整个过程变得更加方便。我们只需要两行代码就可以找到key对应的链表。

hash_key = hash(key) % len(self.headers)
linked_list = self.headers[hash_key]

2.get、put

明白了hash函数的作用了之后,hashmap的问题就算是解决了大半。因为剩下的就是一个在链表当中增删改查的问题了,比如我们要通过key查找value的时候。

当我们通过hash函数确定了是哪一个链表之后,剩下的就是遍历这个链表找到这个值。这个函数我们可以实现在LinkedList这个类当中,非常简单,就是一个简单的遍历。
链表的节点查询逻辑有了之后,hashmap的查询逻辑也就有了。因为本质上只做了两件事,一件事根据hash函数的值找到对应的链表,第二件事就是遍历这个链表,找到这个节点。
因为put方法逻辑和get相反。我们把查找换成添加或者是修改即可。
python库函数实现hashMap

自定义的完整代码:

import random
class Node:
    def __init__(self, key, val, prev=None, succ=None):
        self.key = key
        self.val = val
        # 前驱
        self.prev = prev
        # 后继
        self.succ = succ
 
    def __repr__(self):
        return str(self.val)
 
 
class LinkedList:
    def __init__(self):
        self.head = Node(None, 'header')
        self.tail = Node(None, 'tail')
        self.head.succ = self.tail
        self.tail.prev = self.head
        self.size = 0
 
    def append(self, node):
        # 将node节点添加在链表尾部
        prev = self.tail.prev
        node.prev = prev
        node.succ = prev.succ
        prev.succ = node
        node.succ.prev = node
        self.size += 1
 
    def delete(self, node):
        # 删除节点
        prev = node.prev
        succ = node.succ
        succ.prev, prev.succ = prev, succ
        self.size -= 1
 
    def get_list(self):
        # 返回一个包含所有节点的list,方便上游遍历
        ret = []
        cur = self.head.succ
        while cur != self.tail:
            ret.append(cur)
            cur = cur.succ
        return ret
 
    def get_by_key(self, key):
        cur = self.head.succ
        while cur != self.tail:
            if cur.key == key:
                return cur
            cur = cur.succ
        return None
 
 
 
class HashMap:
    def __init__(self, capacity=16, load_factor=5):
        self.capacity = capacity
        self.load_factor = load_factor
        self.headers = [LinkedList() for _ in range(capacity)]
 
    def get_hash_key(self, key):
        return hash(key) & (self.capacity - 1)
 
    def put(self, key, val):
        hash_key = self.get_hash_key(key)
        linked_list = self.headers[hash_key]
        if linked_list.size >= self.load_factor * self.capacity:
            self.reset()
            hash_key = self.get_hash_key(key)
            linked_list = self.headers[hash_key]
        node = linked_list.get_by_key(key)
        if node is not None:
            node.val = val
        else:
            node = Node(key, val)
            linked_list.append(node)
 
    def get(self, key):
        hash_key = self.get_hash_key(key)
        linked_list = self.headers[hash_key]
        node = linked_list.get_by_key(key)
        return node.val if node is not None else None
 
    def delete(self, key):
        node = self.get(key)
        if node is None:
            return False
        hash_key = self.get_hash_key(key)
        linked_list = self.headers[hash_key]
        linked_list.delete(node)
        return True
 
    def reset(self):
        headers = [LinkedList() for _ in range(self.capacity * 2)]
        cap = self.capacity
        self.capacity = self.capacity * 2
        for i in range(cap):
            linked_list = self.headers[i]
            nodes = linked_list.get_list()
            for u in nodes:
                hash_key = self.get_hash_key(u.key)
                head = headers[hash_key]
                head.append(u)
        self.headers = headers

更通俗的例子

table = {'abc':1, 'def':2, 'ghi':3}
print table

#字典反转
map=dict([(v,k) for k, v in table.iteritems()])
#字典遍历
for key in map.keys():
    print key,":",map[key]

print len(map)
print map.keys()
print map.values()

#字典的增,删,改,查
#在这里需要来一句,对于字典的扩充,只需定义一个新的键值对即可,
#而对于列表,就只能用append方法或分片赋值。
map[4]="xyz"
print map

del map[4]
print map

map[3]="update"
print map

if map.has_key(1):
    print "1 key in"

你可能感兴趣的:(python,哈希算法,散列表)