【问题标题】:Time complexity issues with multimap多图的时间复杂度问题
【发布时间】:2013-03-03 08:54:19
【问题描述】:

我创建了一个程序来查找数字列表的中位数。数字列表是动态的,因为可以删除和插入数字(可以输入重复的数字),在此期间,重新评估并打印出新的中位数。

我使用多图创建了这个程序,因为

1) 已经排序的好处,
2) 轻松插入、删除、搜索(因为multimap实现了二分查找)
3) 允许重复条目。

条目数 + 删除数(表示为 N)的约束为:0

我编写的程序可以运行并打印出正确的中位数,但速度不够快。我知道 unsorted_multimap 比 multimap 快,但是 unsorted_multimap 的问题是我必须对其进行排序。我必须对其进行排序,因为要找到中位数,您需要一个排序列表。所以我的问题是,使用 unsorted_multimap 然后快速对条目进行排序是否可行,或者这很荒谬?只使用向量、快速排序向量并使用二分搜索会更快吗?或者,也许我忘记了一些我什至没有想到的绝妙解决方案。

虽然我对 C++ 并不陌生,但我承认,我在时间复杂性方面的技能有些平庸。


我对自己的问题了解得越多,我就越开始认为仅使用带有快速排序和二进制搜索的向量会更好,因为数据结构基本上已经实现了向量。

【问题讨论】:

  • 我不认为地图是解决这个问题的好数据结构。最佳性能可能来自向量,尽管我没有比较两者。
  • @evanmcdonnal,我认为你可能是对的。我认为向量可能与快速排序和二进制搜索一样快。
  • 我认为这个问题是重复的:stackoverflow.com/questions/1387497/…
  • @JoseLuisBlanco:这不完全是因为该系列可能正在缩小。
  • 好吧,矢量比多图快,但它仍然不够快......这很奇怪。也许我正在以其他方式放慢速度。

标签: c++ big-o time-complexity binary-search multimap


【解决方案1】:

使用矢量几乎肯定会更好。可能会在中位数计算之间维护要删除的索引的辅助向量,以便您可以批量删除它们。新添加的也可以放入辅助向量中,排序,然后合并。

【讨论】:

  • 矢量比多图快,但仍然不够快。我正在使用快速排序和二进制搜索。
【解决方案2】:

我对自己的问题了解得越多,我就越开始认为仅将向量与快速排序和二分搜索一起使用会更好,因为数据结构基本上已经实现了向量。

如果您只有很少的更新 - 使用未排序的 std::vector + std::nth_element 算法,即 O(N)。您不需要 O(N*ln(N)) 的完整排序。

live demo of nth_element:

#include <algorithm>
#include <iterator>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;

template<typename RandomAccessIterator>
RandomAccessIterator median(RandomAccessIterator first,RandomAccessIterator last)
{
   RandomAccessIterator m = first + distance(first,last)/2; // handle even middle if needed
   nth_element(first,m,last);
   return m;
}

int main()
{
   vector<int> values = {5,1,2,4,3};
   cout << *median(begin(values),end(values)) << endl;
}

输出是:

3

如果您有很多更新并且只从中间删除 - 使用两个堆作为comocomocomocomo suggests。如果你使用fibonacci_heap - 那么你也会得到 O(N) 从任意位置移除(如果没有处理它)。

如果您有很多更新并且需要从任意位置删除 O(ln(N)) - 然后使用两个多重集作为 ipc suggests

【讨论】:

    【解决方案3】:

    如果您的目的是动态跟踪中位数,在插入/删除元素时,您应该使用最小堆和最大堆。每个都包含一半的元素...前几天有一个相关的问题:How to implement a Median-heap

    但是,如果您需要搜索特定值以删除元素,您仍然需要某种映射。

    你说它很慢。每次需要中位数时,您是否都从地图的开头迭代到第 (N/2) 个元素?你不需要。您可以通过维护一个始终指向它的迭代器和一个小于该元素数量的计数器来跟踪中值。每次插入/删除时,将新/旧元素与中值进行比较,并更新迭代器和计数器。

    另一种看待它的方式是两个多重映射,每个映射包含一半的元素。一个持有小于中位数(或相等)的元素,另一个持有大于中位数的元素。堆更有效地执行此操作,但它们不支持搜索。

    如果您只需要几次中位数,您可以使用“选择”算法。它在 Sedgewick 的书中有所描述。平均需要 O(n) 时间。它类似于快速排序,但不完全排序。它只是用随机枢轴对数组进行分区,直到最终它在一侧“选择”较小的 m 个元素 (m=(n+1)/2)。然后搜索这 m 个元素中最大的一个,这就是中位数。

    【讨论】:

      【解决方案4】:

      这是每次更新在O(log N) 中实现该功能的方法:

      template <typename T>
      class median_set {
      public:
        std::multiset<T> below, above;
      
        // O(log N)
        void rebalance()
        {
          int diff = above.size() - below.size();
          if (diff > 0) {
            below.insert(*above.begin());
            above.erase(above.begin());
          } else if (diff < -1) {
            above.insert(*below.rbegin());
            below.erase(below.find(*below.rbegin()));
          }
        }
      
      public:
        // O(1)
        bool empty() const { return below.empty() && above.empty(); }
      
        // O(1)
        T const& median() const
        {
          assert(!empty());
          return *below.rbegin();
        }
      
        // O(log N)
        void insert(T const& value)
        {
          if (!empty() && value > median())
            above.insert(value);
          else
            below.insert(value);
          rebalance();
        }
      
        // O(log N)
        void erase(T const& value)
        {
          if (value > median())
            above.erase(above.find(value));
          else
            below.erase(below.find(value));
          rebalance();
        }
      };
      

      (Work in action with tests)

      思路如下:

      • 跟踪两组中高于和低于中值的值
      • 如果添加了新值,请将其添加到相应的集合中。始终确保下面的集合正好比另一个多 0 或 1
      • 如果删除了某个值,请将其从集合中删除并确保条件仍然成立。

      您不能使用priority_queues,因为他们不允许您删除一项。

      【讨论】:

      • "您不能使用 priority_queues,因为它们不允许您删除一项。" - fibonacci_heap 支持擦除元素。它的操作是:top - O(1),push - O(1),erase - O(ln(N))。但是搜索要擦除的元素(如果没有处理它)是 O(N),所以擦除将是 O(N)。 boost.org/doc/libs/1_53_0/doc/html/heap/data_structures.html
      【解决方案5】:
      Can any one help me what is Space and Time complexity of my following C# program with details.
      //Passing Integer array to Find Extreme from that Integer Array
         public int extreme(int[] A)
              {
                  int N = A.Length;
                  if (N == 0)
                  {
                      return -1;
                  }
                  else
                  {
                      int average = CalculateAverage(A);
                      return FindExtremes(A, average);
                  }
              }
      // Calaculate Average of integerArray
              private int CalculateAverage(int[] integerArray)
              {
                  int sum = 0;
                  foreach (int value in integerArray)
                  {
                      sum += value;
                  }
                  return Convert.ToInt32(sum / integerArray.Length);
              }
      //Find Extreme from that Integer Array
              private int FindExtremes(int[] integerArray, int average) {
                  int Index = -1; int ExtremeElement = integerArray[0];
                  for (int i = 0; i < integerArray.Length; i++)
                  {
                      int absolute = Math.Abs(integerArray[i] - average);
                      if (absolute > ExtremeElement)
                      {
                          ExtremeElement = integerArray[i];
                          Index = i;
                      }
                  }
                  return Index;
              }
      

      【讨论】:

        猜你喜欢
        • 2021-06-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多