Dart和Flutter的有效算法:哈希集。
在Dart的有效算法,第一部分,我们开始谈论Dart和Flutter中的算法和数据结构。我们提出了一个想法:算法很重要。即使在非常简单的情况下,选择适当的算法也是至关重要的。
今天我们将讨论选择适当的数据结构的重要性。今天这节课的口号是数据结构很重要。
像往常一样,我们的旅程将是实际的,在在线Flutter IDE的帮助下。
因此,让我们在这里挑选初始的Flutter项目。
点击 "Fork to FlutLab IDE"。你现在应该在你的仪表板上看到一个名为 "Algorithms matter 2 "的新Flutter项目。
打开这个项目并运行Web构建。
现在我们为我们的旅程做好了准备!
我们将尝试编写一个非常简单的应用程序。这个应用程序将对数组内的一些数字进行搜索。让我们看看我们的Flutter应用程序的构建方法。
它从Algo类中创建一个algo对象。Algo的构造函数有2个参数。
- 要搜索的数组
- 要查询的值
然后,我们的应用程序将algo.perform()的结果渲染在应用程序屏幕中央的文本内。老实说,它的搜索工作做得很差--它说在数组[1, 2, 3 ,4]中没有找到数字4。
让我们看看位于main.dart附近的algo.dart 文件。
现在一切都清楚了!Algo有硬编码的执行方法。它一直在返回false。
让我们用最简单的方法解决这个问题。
这看起来是一个合适的解决方案。让我们用这个按钮运行我们的单元测试。
在这里我们可以看到一些问题。
这里是失败的测试。
所以我们期望查找功能的执行速度快于10毫秒,但却花了623分钟。
解决这个问题的方法是改变我们的查询的数据结构。
让我们改变我们的实现,在执行方法中使用Set而不是List。
如果我们现在运行我们的单元测试--它们将成功通过。
让我们来分析一下为什么Set在查找时如此快速。但首先,让我们估计一下我们第一次使用List集合实现查找的时间复杂性。平均来说,对于N个元素,我们需要花N/2步来寻找列表中的值。我们只是从第一个元素到最后一个元素,一步一步来。这是一个O(n)算法,因为我们必须放弃所有的常数(详见第一部分)。
那么,能允许O(1)搜索的野兽是什么呢?我们看到,这头野兽是一个集合。更确切地说,是一个哈希集合。
但是在深入研究哈希集之前,让我们从另一个角度来看看列表数据结构。我们把它叫做索引表。我们可以把一些列表,比如**[5, 2, 6, 4]画成一个有两列的表:索引和值**。
其中索引--是我们的值在列表中的位置。事实上,有了某个值的索引,我们可以用**O(1)**的时间复杂度得到它--我们可以直接跳到相应的存储单元。事实上,因为我们所有的元素都有相同的大小(整数值)--我们可以直接用索引乘以单个元素的大小。
如果我们可以把值作为自己的一个键呢?我们刚刚发现了哈希集的主要思想!
想象一下,有一个特殊的数学函数,它可以获得数值,比如说5,并计算出它在我们集合中的位置。这个函数有一个特殊的名字:Hash。哈希函数可以非常简单。例如,它可以是一个简单的算法。
- 获取一个值的字符串表示
- 将这个字符串分割成各个字符
- 将这些字符映射成它们的ASCII数字
- 返回这些ASCII数字的总和
因此,对于我们简单的值5,我们得出了一个数字53 (谷歌ASCII表)。这是在我们自己的Hash Set实现中的值5的一个地址。
当然,生产哈希函数要复杂得多。它们结合了计算的速度和有效的碰撞算法。是的,碰撞是会发生的!想象一下两个值。53和35。我们自己卑微的Hash函数会把它们放到Hash Set里面的一个桶里。是的,碰撞解决需要在桶内进行O(n)的交互。好的Hash函数会在内存中足够小的空间内均匀地放置你的值。
在这一点上,你应该保持对哈希集是如何工作的一般理解,以及为什么它对搜索算法如此重要。
你可以跳转到FlutLab,玩玩本课的代码,阅读一些关于Dart集合方法的资料。
总结:算法和数据结构是一个复杂而广泛的知识库。然而,我们可以实际地学习它。其方法和术语的关键、小的子集使我们能够写出更有效的代码并通过编码面试。今天我们学习了如何使用适当的数据结构来加速搜索算法。哈希集。我们已经对哈希函数有了一个概念。