当前位置: 首页 > 知识库问答 >
问题:

合并K个排序数组/向量的复杂性

薛弘厚
2023-03-14

在研究合并k个排序的连续数组/向量的问题以及它在实现上与合并k个排序的链表有何不同时,我发现了两个相对简单的用于合并k个连续数组的朴素解决方案和一个基于成对合并的很好的优化方法,该方法模拟了mergeSort()的工作原理。我实现的两个朴素解决方案似乎具有相同的复杂性,但在我进行的一个大型随机测试中,似乎一个比另一个效率更低。

我天真的合并方法如下所示。我们创建一个输出向量

vector<int> mergeInefficient(const vector<vector<int> >& multiList) {
  vector<int> finalList = multiList[0];
  for (int j = 1; j < multiList.size(); ++j) {
    finalList = mergeLists(multiList[j], finalList);
  }

  return finalList;
}

我的第二个天真解决方案的工作原理如下:

/**
 * The logic behind this algorithm is fairly simple and inefficient.
 * Basically we want to start with the first values of each of the k
 * vectors, pick the smallest value and push it to our finalList vector.
 * We then need to be looking at the next value of the vector we took the
 * value from so we don't keep taking the same value. A vector of vector
 * iterators is used to hold our position in each vector. While all iterators
 * are not at the .end() of their corresponding vector, we maintain a minValue
 * variable initialized to INT_MAX, and a minValueIndex variable and iterate over
 * each of the k vector iterators and if the current iterator is not an end position
 * we check to see if it is smaller than our minValue. If it is, we update our minValue
 * and set our minValue index (this is so we later know which iterator to increment after
 * we iterate through all of them). We do a check after our iteration to see if minValue
 * still equals INT_MAX. If it has, all iterators are at the .end() position, and we have
 * exhausted every vector and can stop iterative over all k of them. Regarding the complexity
 * of this method, we are iterating over `k` vectors so long as at least one value has not been
 * accounted for. Since there are `nk` values where `n` is the average number of elements in each
 * list, the time complexity = O(nk^2) like our other naive method.
 */
vector<int> mergeInefficientV2(const vector<vector<int> >& multiList) {
  vector<int> finalList;
  vector<vector<int>::const_iterator> iterators(multiList.size());

  // Set all iterators to the beginning of their corresponding vectors in multiList
  for (int i = 0; i < multiList.size(); ++i) iterators[i] = multiList[i].begin();

  int k = 0, minValue, minValueIndex;

  while (1) {
    minValue = INT_MAX;
    for (int i = 0; i < iterators.size(); ++i){
      if (iterators[i] == multiList[i].end()) continue;

      if (*iterators[i] < minValue) {
        minValue = *iterators[i];
        minValueIndex = i;
      }
    }

    iterators[minValueIndex]++;

    if (minValue == INT_MAX) break;
    finalList.push_back(minValue);
  }

  return finalList;
}

长话短说,我构建了一个简单的随机模拟,它构建了一个多维向量

clock_t clock_a_start = clock();
finalList = mergeInefficient(multiList);
clock_t clock_a_stop = clock();

clock_t clock_b_start = clock();
finalList = mergeInefficientV2(multiList);
clock_t clock_b_stop = clock();

然后,我构建了以下绘图:

我的计算表明,两个朴素的解决方案(合并和选择)都具有相同的时间复杂度,但上面的图显示它们非常不同。起初,我通过说一个与另一个可能有更多的开销来合理化这一点,但后来意识到开销应该是一个常数因子,不会产生如下图。对此有什么解释?我假设我的复杂性分析是错误的?


共有1个答案

池麒
2023-03-14

即使两个算法具有相同的复杂性(在您的情况下是O(nk^2)),它们最终的运行时间可能会有很大的不同,这取决于您的输入大小和所涉及的“常量”因素。

例如,如果一个算法在1000时间内运行,而另一个算法在1000时间内运行,它们都具有相同的渐近复杂度,但它们的运行时间应非常不同,以便“合理”选择n。

此外,缓存、编译器优化等可能会显著改变运行时间。

对于您的情况,虽然您对复杂性的计算似乎是正确的,但在第一种情况下,实际运行时间应为(nk^2 nk)/2,而在第二种情况下,运行时间应为nk^2。请注意,除以2可能很重要,因为随着k的增加,nk项可以忽略不计。

对于第三种算法,可以通过维护包含所有k个向量的第一个元素的k个元素堆来修改原始选择。然后,您的选择过程将花费O(logk)时间,因此复杂性将降低到O(nklogk)

 类似资料:
  • 我想写一个时间O(n*lgk)的算法,将k个排序数组合并成一个排序数组,其中n是所有输入数组的元素总数。 你能告诉我怎么做吗? 编辑:我编写了以下算法: 你能告诉我这是否正确吗?

  • 两个反向数组合并成一个排序数组的时间复杂度是多少? 是O(n)还是O(log n)?

  • 问题: 我必须分析时间复杂度来对几乎已排序的整数值列表进行排序(使用快速排序)。 我做了什么? 我读过SO Q1、SO Q2、SO Q3和这一本。 但是,我没有发现任何明确提到使用快速排序对k排序数组进行排序的时间复杂度的内容。 由于快速排序算法的时间复杂度取决于选择数据透视的策略,并且由于几乎排序了数据,因此有可能面临最坏情况,为了避免最坏情况,我使用了三个值(第一、中间、最后)的中位数作为这里

  • 我收到一份作业,要求我将总共有N个元素的K个排序列表有效地合并到一个排序列表中。我偶然发现的方法是使用最小堆对K列表中的元素进行排序,或者使用分而治之的方法(成对合并)。该线程中的注释表明,分而治之方法的时间复杂度为O(NK),而最小堆方法的时间复杂度为O(N log K),两者的空间复杂度相同。我还访问了许多其他线程,但我不能得到一个清晰的图片。 怀疑 许多其他网站告诉我们,两者都存在分歧

  • 问题内容: 给定两个排序数组,如下所示: 我希望输出为: 要么: 我知道我可以执行以下操作: 我只是想知道是否有一种更快的方法,因为我要处理的数组具有数百万个元素。 任何想法都欢迎。谢谢 问题答案: 由于您使用numpy,因此我怀疑bisec根本不会对您有所帮助。因此,我建议您做两件事: 千万 不能 使用,使用方法,而不是这种种取代阵列,避免了复制。 必须使用没有到位的。因此,不要手动使用逻辑。I