【问题标题】:Grouping elements of a list efficiently有效地对列表的元素进行分组
【发布时间】:2016-07-12 10:03:44
【问题描述】:

给定一个元素列表,按某个键对元素进行分组的最有效方法是什么?

例如,如果输入是{a,c,a,b,c,b,b},则有效输出可能是{a,a,b,b,b,c,c}{b,b,b,c,c,a,a}

显然,如果键的类型遵循顺序,则可以使用排序算法,这将使我们得到O(nlogn) 的复杂度。

但是,密钥可能仅在相等性方面可比较,而在顺序方面则不可比较。例如,typeof(dog) != typeof(cat),但询问 typeof(dog) < typeof(cat) 是没有意义的。

当然,可以强制排序(例如obj.GetType().ToString() 并使用词法顺序),但由于不需要严格的顺序(仅分组),我想知道是否有比排序更有效的方法。

【问题讨论】:

  • 你有多少钥匙? O(n) 或更小?
  • 您想要“就地”分组,还是复制元素并制作另一个排序列表就可以了?
  • @OhadEytan 键的数量不同,但通常接近 1 或接近 n。我更喜欢“就地”算法,因为此操作将在许多输入上完成。
  • 如果键可以分解成有序的片段序列,每个片段只能取少量值(例如,字符 1、字符 2 等),那么您可以 radix对它们进行排序(请注意,基数排序实际上涉及任何<> 比较)。像 Fabel 的散列解决方案可以看作是一种基数排序的第一步。
  • OTOH 如果必须将密钥视为黑匣子,我相信您能做的最好的事情就是建立一个等价类列表,如下所示:从一个空的类 L 列表开始。对于每个项目 x,将 x 与 L 中每个类的一些(任何)项目进行比较。如果这些相等测试中的任何一个成功,则将 x 添加到该类;如果它们都失败了,则向 L 添加一个仅包含元素 x 的新类。处理完所有项目后,L 包含每个组的一个类。这显然具有最坏情况的时间复杂度 O(n^2),当所有项目都不同时会发生这种情况。

标签: algorithm performance sorting time-complexity


【解决方案1】:

我会使用未排序的链表和哈希映射来插入点,如下所示(在伪代码中):

function group_sort(unsorted_list)
  node_map = new hash_map
  sorted_list = new linked_list
  foreach node in unsorted_list
    last_node = node_map.get(type_of(node))
    if last_node found
      sorted_list.insert_after(last_node,node)
    else
      sorted_list.append(node)
      node_map.add(type_of(node),node)
  delete node_map
  return sorted_list

哈希映射查找和插入大多是 O(1),根据您的数据,哈希映射插入甚至可能很少见。链表插入总是 O(1)。这使得函数几乎是 O(n),当然也是 O(nlogn)。

【讨论】:

  • 好主意。但是,列表插入只有在实现为双向链表时才为 O(1)。如果底层存储是一个数组,这通常是流行的首选实现的情况(例如 C# 的 List,Java 的 ArrayList),那么插入是 O(n)。
  • @MikeM:在基于数组的列表中间插入一个元素是 O(n),但是像 Fabel 在这里所做的那样将它附加到末尾是 O( 1)(摊销)。此外,在单链表的前面插入是 O(1)。
  • 我的例子在中间和最后都插入,都是 O(1)。需要明确的是,我说的是链表,而不是数组。使用数组进行排序是非常低效的,并且会使这个函数 O(n2)。
【解决方案2】:

您可以将每个项目插入multiset

【讨论】:

    猜你喜欢
    • 2017-06-06
    • 2018-01-10
    • 1970-01-01
    • 1970-01-01
    • 2021-10-14
    • 2015-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多