最近,当我需要遍历std::map
时,我通常会从地图构建std::vector
,因为访问元素的复杂性是(log N)(事实上,只有当我需要时通过自定义键访问元素。)
所以,我最终维护一个std::vector
只是为了迭代我的所有元素,(因为它的复杂性是常数O(1))和一个std::map
用于特定检索(因为它的复杂性是O( log n)迭代时。)
这是一种正确的方法吗,或者我应该使用std::map
并忘记std::vector
(你知道,对于这个具体案例)。
谢谢。
答案 0 :(得分:7)
除非分析告诉您需要,否则您应该忘记vector
。每个迭代器增量在map
中迭代不是O(log 2 N)...它只需要通过遍历平衡二进制中当前元素的最小路径来查找下一个元素树...通常只跟随一个或两个链接,尽管从最后一个左侧节点移动到第一个右侧节点的最坏情况步骤需要2 *(log 2 (N )-1)移动,如果它只是使用通用方法。
要想象这一点,请考虑下面地图中的节点 - 它们总是按排序顺序排列,这里我假设数据元素恰好是递增整数,因为我们可以使用它们的值轻松地引用节点: / p>
8
/ \
4 12
/ \ / \
2 6 10 14
/ \ / \ / \ / \
1 3 5 7 9 11 13 15
当您遍历时,您的迭代器可能必须从根“8”开始并将左侧分支遍历为“1”,然后它将单个链接向上移动到2,然后向下移动到单个链接到“3”之前,必须弹出几个链接到“4”,然后将一对夫妇弹出到“5”。很明显 - 大部分时间它只是跟随1或2个链接,更长的路径越来越罕见:如果我们全部列出:
6 links: once/x1: 7-6-4-8-12-19-9
3 links: 8-1
2 links: x4: 3-2-4, 4-6-5, 11-10-12, 12-14-13
1 link: x8: 1-2, 2-3, 5-6, 6-7, 9-10, 10-11, 13-14, 14-15
总计是8 * 1 + 2 * 4 + 3 * 1 + 6 * 1 = 25 ... 25个链接遍历迭代15个元素。
该序列可以推广为任意N,如下:N / 2 + 4N / 8 + 6N / 16 + 10N / 32 + 12N / 128 + 14N / 256 ... 2iN / 2 i + 1 如果我们简化分数并除以N,我们得到一个系列:
1/2, 1/2, 3/8, 1/4, 5/32, 3/32, 7/128, ...
有很多proofs here它转换为2N,即迭代器的每个增量的平均链接数收敛到大N的2,而2是常数因子,我们仍然可以说增加迭代器是O(1)。