【问题标题】:Nested loop complexity嵌套循环复杂度
【发布时间】:2014-12-04 11:29:00
【问题描述】:

我有几个不同大小的列表,列表的每个索引都包含一个键和一个对象:list1.add('Key', obj)。

所有列表都已排序。

我的目标是遍历列表,并使用键将列表 2、3、4 或 n 中的 1 个或多个项目与 mainList 中的项目匹配。

目前我在做一些事情:

for i to list1 size
   for j to list2 size
   if list1[i] = list2[j]
      do stuff 

当我遍历 I'm 时,使用布尔值快速退出,使用 if current != 先前的检查,我正在从列表中删除对象。

它工作正常,但我现在有另一个需要匹配的列表,之后可能还有另一个 n 列表。列表大小不同。

我可以看到的 2 个选项是在更改内部列表的情况下多次重复上述代码段 - 我不喜欢这种方法。

另一种选择是扩展上述内容,一旦完成一个内循环,就进入下一个:

  for i to list1 size
       for j to list2 size
       if list1[i] = list2[j]
          do stuff 
       for k to list2 size
       if list1[i] = list2[k]
          do stuff 

我认为我认为第二个更有效的想法是正确的,但我不确定。还有,有没有更好的办法?

感谢您的任何建议/帮助。

【问题讨论】:

  • 它们的时间复杂度相同。第一个选项是O(i*j) + O(i*k),第二个是O(i*(j+k))
  • @Barmar :谢谢。那么复杂度是 n^2 吗?我认为在某些情况下,它可能会有所改进,但复杂性分析不是我的强项:)
  • 这个问题似乎是题外话,因为 cs.stackexchange.com 是解决此类问题的更好地方。
  • @Barmar :谢谢,我没有意识到它会被视为离题。除非您想根据评论发布答案,否则我现在将其移动

标签: algorithm list loops time-complexity


【解决方案1】:

如果列表都已排序,那么您只需要遍历每个列表一次;在主列表的每次迭代中,遍历一个二级列表(从之前保存的索引开始,初始化为 0),直到找到一个值大于主列表当前值的索引,保存该索引,然后继续下一个二级列表。

Array<Integer> indices = new Array(n-1); // indices for every list except list1
for(int i = 0; i < indices.size; i++) {
  indices[i] = 0;
}

for(int i = 0; i < list1.size; i++) {
  Value curVal = list1[i];
  while(indices[0] < list2.size && list2[indices[0]] <= curVal) {
    if(list2[indices[0]] == curVal) {
      // do stuff on list2[indices[0]]
    }
    indices[0]++;
  }
  while(indices[1] < list3.size && list3[indices[1]] < curVal) {
    if(list3[indices[1]] == curVal) {
      // do stuff on list3[indices[1]]
    }
    indices[1]++;
  }
  // etc
}

您可以使用 ListIterator 之类的东西来避免复制粘贴,其中包含一个列表及其当前索引;然后在主循环的每次迭代中,您将遍历 ListIterators 列表以代替复制粘贴的代码块

public class ListIterator {
  int index = 0;
  List<Value> list;
  Value curVal() {
    return list[index];
  }
  boolean hasNext() {
    return (index < list.size);
  }
}

List<ListIterator> iterators;
for(int i = 0; i < list1.size; i++) {
  Value curVal = list1[i];
  for(int j = 0; j < iterators.size; j++) {
    ListIterator iterator = iterators[j];
    while(iterator.hasNext() && iterator.curVal() <= curVal) {
      if(iterator.curVal() == curVal) {
        // do something with iterator.curVal()
      }
      iterator.index++;
    }
  }
}

这是时间复杂度 O(n),其中 n 是所有列表长度的总和


编辑:如果难以通过&lt;= 比较密钥,那么您可以改用Set 实现。将 List1 键添加到 Set,然后遍历其余列表以测试集合成员资格。

Set<String> set = new Set(List1);
Array<List> lists = new Array();
// add lists to Array<List>

for(int i = 0; i < lists.size; i++) {
  List currentList = lists[i];
  for(int j = 0; j < currentList.size; j++) {
    if(set.contains(currentList[j]) {
      // do something
    }
  }
}

【讨论】:

  • 嗨,我只是在考虑实施您的解决方案。不幸的是,我无法访问迭代器;旧版 Delphi 的乐趣。你在哪里做 '&& list2[indices[0]]
  • @SteveGreen 密钥是如何排序的?给定您的示例,它看起来可能是“如果 key1 的 alpha 前缀小于 key2 的 alpha 前缀,则 key1 list2[indices[0]] <= curVal 将成为更复杂的isLessThan(list2[indices[0]], curVal),您可以在其中拆分和比较密钥
  • @SteveGreen 如果编写isLessThan 方法不可行,那么如果您可以访问Set 数据结构(这可以实现为MapDictionary 与null values) 然后您可以将所有 List1 键添加到集合中,然后遍历所有其他列表,测试集合成员资格。 list2[indices[0]] &lt;= curVal 变为 set.contains(list2[i]) - 您不再需要 indices,因为您可以一次遍历每个二级列表。这仍然是平均 O(n)
  • @SteveGreen This answer 提供了一个很好的散列函数的 Delphi 实现。分配一个名为 set 的数组,该数组比 List1.size 大,通常会大 25% 到 100%,具体取决于您的内存限制(数组越大,冲突越少)。该数组包含字符串的链表。向数组添加字符串时,计算int key = hash(string) modulo set.size,并将字符串追加到链表set[key]。查找也是如此 - 计算键,然后遍历链表进行精确匹配
  • @SteveGreen 使用链表处理冲突是一种“封闭寻址”方案; “开放寻址”方案是在检测到冲突时将字符串添加到相邻数组元素的方案(对于“相邻”的某些定义 - 不同的开放寻址方案使用不同的探测技术)。请参阅hopscotch hashing 上的 Wikipedia 文章,了解特定的开放寻址方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多