微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

顺序扫描数组与链表的时间复杂度?

如何解决顺序扫描数组与链表的时间复杂度?

由于数组的元素连续存储在内存中,因此我了解到顺序扫描数组的所有元素比在相同大小的链表中要快得多。对于前者,您只需要增加一些索引变量,然后读取该索引处的值,而对于LL,则您必须读取指针,然后在不连续的内存地址处获取数据。我的问题是具体如何根据时间复杂度对这两种情况进行分类

对于扫描阵列,是否执行 n *次随机访问,即O(1)运算是否意味着整体将变为O(n)?在那种情况下,不是都是O(n)吗?

很抱歉,如果这个问题没有道理,也许我对时间复杂性的理解不是很好。

解决方法

您是正确的

  1. 依次扫描链接列表或数组需要时间O(n),并且
  2. 由于引用的局限性,扫描数组比链接列表要快得多。

怎么可能?这个问题与您使用该O(n)计算的数量有关。通常,在对算法进行分析时,我们假设在内存中查找位置需要时间O(1),并且由于在两种情况下都在执行O(n)内存查找,所以总时间为O(n)。 / p>

但是,所有内存查找都花费相同时间的假设在实践中并不是一个很好的假设,尤其是对于链接结构。有时您会看到以不同方式执行的分析。我们假设在机器的某个地方有一个可以在任何时候保存B个元素的缓存。每次我们进行内存查找时,如果它在高速缓存中,则(基本上)是免费的,并且如果不在高速缓存中,则我们需要进行一些工作以将该内存地址以及该位置周围的内存内容加载到高速缓存中。然后,我们只关心需要多少次将某些内容加载到缓存中,因为这样可以更准确地预测运行时间。

对于链表,其中的单元格可以随机散布在整个内存中,我们希望在扫描链表时进行Θ(n)内存转移,因为我们基本上从不希望拥有链表单元已经在缓存中。但是,对于数组,每当我们发现不在缓存中的数组元素时,我们都会将所有相邻的内存位置拉入缓存,这意味着接下来的几个元素肯定会在缓存中。这意味着仅(大约)每1 / B查找将触发高速缓存未命中,因此我们希望进行Θ(n / B)存储器传输。从理论上可以预测我们的经验-相继扫描数组比链表要快得多。

总而言之,这实际上是您要测量的内容以及如何测量它的问题。如果仅对内存访问进行计数,那么两个顺序访问都将需要O(n)个内存访问,因此要花费O(n)的时间。但是,如果您只关心高速缓存未命中,那么对动态数组的顺序访问需要Θ(n / B)传输,而链表扫描需要Θ(n)传输,这就是为什么链表看起来更慢的原因。 >

在设计数据库的数据结构时经常使用这种分析。在数据库和文件系统中广泛使用的B树(及其相对的B +树)经过专门调整以围绕磁盘页面的大小以减少内存传输。已经进行了更多的工作来设计“缓存易懂的”数据结构,即使在不知道缓存大小的情况下,该数据结构也始终可以最佳利用缓存。

,

不幸的是,您错过了这些事情的工作方式。

  • 顺序扫描所有数组元素为O(n),大小为n 数组,因为您将访问每个元素。您将需要计算每个地址,并n次提取数据;
  • 顺序扫描所有链表元素是O(n),是链表大小的n,因为您将通过链接访问每个元素;
  • 数组的一个元素为O(1),因为访问与一个内存地址计算和一个获取过程有关;
  • 在链接的对象中,要访问的位置是n,因此链接的lisk中的一个元素为O(n),因为您需要到达第n个元素,使每个链接都跳,直到到达所需的元素。 / li>
,

访问某个索引(例如500)的值在数组中是“立即”(O(1)),而使用链接列表,则必须迭代500多个节点才能获得想要的节点(O( n))。

因此,使用数组时,可以以相同的速度访问容器开头或结尾的索引,而使用链表,索引越高,则获取索引越耗时。

相反,在链表中插入节点既容易又快速,而在数组中插入节点则较慢。

那么问题就变成了什么是更常见的操作:访问索引(写,读)或操作容器结构(插入,删除)?答案似乎很明显,但在某些情况下可能并非如此。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。