在python中排序 、 哈希表

1,754 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情

在python中排序

大多数Python实现中使用的排序算法称为timsort.7°关键思想是利用这样一个事实,即在许多数据集中,数据已经部分排序。Timsort 最坏情况的性能与合并排序的性能相同,但平均而言,它的性能要好得多。

如前所述,Python方法1ist。sort 将列表作为其第一个参数并修改该列表。相比之下,python函数 sorted 将可迭代对象(例如,列表或视图)作为其第一个参数,并返回新的排序列表。例如,代码

image.png

打印

image.png

请注意,当排序函数应用于字典时,它将返回字典键的排序列表。相反,当排序方法应用于字典时,它会导致引发异常,因为没有方法 dict.sort。

list.sort 方法和排序函数都可以有两个附加参数。在我们的合并排序实现中,key 参数的作用与比较相同:它提供了要使用的比较函数。reverse 参数指定相对于比较函数是按升序还是降序对列表进行排序。例如,代码

image.png

按长度的相反顺序对 L 的元素进行排序,并打印

image.png

list.sort 方法和排序函数都提供稳定的排序。这意味着,如果两个元素相对于排序中使用的比较(在本例中为 1en)相等,则它们在原始列表(或其他可迭代对象)中的相对顺序将保留在最终列表中。(因为任何密钥都不能出现超过一次一个字典,排序在应用于字典时是否稳定的问题是没有实际意义的。

练习:merge_sort是一种稳定吗?

哈希表

如果我们将合并排序与二进制搜索放在一起,我们有一个很好的方法来搜索列表。我们使用合并排序按 θ(n1og(n)) 时间的顺序预处理列表,然后使用二进制搜索来测试元素是否按 θ1og (n) ) 时间顺序出现在列表中。如果我们搜索列表 k 次,则总时间复杂度为阶 θ(n1og(n) + k*1og(n) )。这很好,但我们仍然可以问,当我们愿意做一些预处理时,对数是我们能为搜索做的最好的吗?当我们在第 5 章中介绍类型字典时,我们说过字典使用一种称为哈希的技术来执行几乎与字典大小无关的时间查找。哈希表背后的基本思想很简单。我们将键转换为整数,然后使用该整数索引到列表中,这可以在常量时间内完成。原则上,任何类型的值都可以轻松转换为整数。毕竟,我们知道每个对象的内部表示都是一个位序列,任何位序列都可以被视为表示整数。例如,字符串“abc”的内部表示形式是011000010110001001100011位序列,可以将其视为十进制整数 6,382,179 的表示形式。当然,如果我们想将字符串的内部表示作为索引使用到列表中,那么列表必须很长。

如果键已经是整数,该怎么办?想象一下,目前,我们正在实现一个字典,其所有密钥都是美国社会安全号码,即九位整数。如果我们用一个包含 109 个元素的列表来表示字典,并使用社会安全号码来索引到列表中,我们可以在恒定时间内进行查找。当然,如果字典只包含一万(104)人的条目,这将浪费相当多的空间。

这让我们想到了哈希函数的主题。哈希函数将较大的输入空间(例如,所有自然数)映射到较小的输出空间(例如,o和5000之间的自然数)。哈希函数可用于将较大的键空间转换为较小的整数索引空间。

由于可能输出的空间小于可能输入的空间,因此哈希函数是多对一映射,即多个不同的输入可以映射到同一输出。当两个输入映射到同一输出时,它被称为冲突 - 我们很快就会回到这个主题。一个好的哈希函数产生均匀分布;即,该范围内的每个输出都具有相同的可能性,从而将碰撞的可能性降至最低。

图 12-7 使用一个简单的哈希函数(回想一下,当整数 i 除以整数 j 时,i%j 返回余数)来实现以整数作为键的字典。

基本思想是通过哈希存储桶列表表示类Int_dict的实例,其中每个存储桶都是作为元组实现的键/值对的列表。通过将每个存储桶设置为一个列表,我们通过存储列表中散列到同一存储桶的所有值来处理冲突。

哈希表的工作原理如下:实例变量存储桶初始化为num_buckets空列表的列表。要存储或查找带有键键的条目,我们使用哈希函数 。将键转换为整数,并使用该整数将索引到存储桶中,以查找与键关联的哈希存储桶。然后,我们线性搜索该存储桶(请记住,这是一个列表),以查看是否存在带有 key 键的条目。如果我们正在执行查找并且有一个带有键的条目,则只需返回与该键一起存储的值。如果没有该钥匙的入口,我们将返回无。如果要存储一个值,我们首先检查哈希存储桶中是否已经有一个具有该键的条目。如果是这样,我们将条目替换为新的元组;否则,我们将新条目附加到存储桶。

还有许多其他方法可以处理冲突,其中一些方法比使用列表更有效。但这可能是最简单的机制,如果哈希表相对于其中存储的元素数量足够大,并且哈希函数为均匀分布提供了足够好的近似值,则它可以正常工作。

image.png

请注意,the_str__方法生成的字典表示形式与元素添加到其中的顺序无关,而是按键碰巧哈希的值进行排序。

以下代码首先构造一个包含 17 个存储桶和 20 个条目的 Int 字典。条目的值为整数 o 到 19。使用随机随机选择键。选择,从 o 到 105 - 1 范围内的整数。然后,代码使用

str_method类中定义。

image.png

当我们运行此代码时,它打印了79 Int字典的值为:

image.png

以下代码通过迭代 D. 存储桶来打印各个哈希存储桶。(这是对信息隐藏的可怕违反,但在教学上是有用的。

打印('桶是:')

image.png

当我们运行此代码时,它打印了存储桶是:

image.png

当我们违反抽象屏障并窥视Int_dict的表示形式时,我们会看到一些哈希桶是空的。其他条目包含一个或多个条目,具体取决于发生的冲突数。

get_value的复杂性是什么?如果没有冲突,则为常量时间,因为每个哈希桶的长度为 o 或 1。但是,当然,可能会有碰撞。如果所有内容都散列到同一个存储桶,则字典中的条目数将是线性的,因为代码将对该哈希存储桶执行线性搜索。通过使哈希表足够大,我们可以充分减少冲突的数量,以允许我们将复杂性视为常量时间。也就是说,我们可以用空间换取时间。但权衡是什么呢?