每日编码问题1.4:解决方案中给出的运行时间不正确?

时间:2019-04-24 18:58:03

标签: algorithm performance


可行

我正在研究一本名为《每日编码问题》的书中的特定难题。这是第一章中的第四个问题。

问题陈述是:

Given an array of integers, return a new array where each element 
in the new array list is the number of smaller elements to the 
right of that element in the original input array.

他们提出了幼稚的解决方案,该解决方案将遍历数组中每个元素的元素并进行适当计数。当然,这是O(n ^ 2)时间。

但是,他们声称有一种解决方案可以在O(nlogn)时间内运行。所以几天来,我一直在挠头。最后,出于不耐烦和沮丧,我提出了一些不同的解决方案,但都没有改善O(n ^ 2),因此我研究了他们的解决方案。


我的问题:

他们的解决方案正是我提出的解决方案之一。但是,当我考虑此解决方案的运行时时,我发现这是O(n ^ 2)时间,而不是O(long)时间,就像他们声称的那样。

我希望您的输入:)

算法:

  1. 向后遍历输入列表
  2. 维护到目前为止所见元素的排序列表
  3. 查看当前元素,以了解在从头到尾进行操作时正在构建的排序数组中的位置。

“分析:”

对于n元素数组中的每个元素,

  1. 该算法搜索正在构造的排序数组,但找不到该元素的位置O(logn)
  2. 该元素插入到数组O(n)中(正在构造的整个排序数组可能必须移动)。

因此,对于n元素数组中的每个元素,搜索该元素的正确位置并将该元素插入正在构造的已排序数组中的操作将为O(logn + n)= O(n),并且,因此整个算法将为O(n * n)。

例如,如果给定数组

 1 2 3 4 5 6 7 8 9 10

将每个元素插入我们要维护(构造)的排序数组中都需要进行移位。

我不正确吗?

感谢您的时间和反馈:)

1 个答案:

答案 0 :(得分:2)

您是正确的,但如果使用二进制堆进行插入,则不对。本质上是在进行堆排序。

https://en.wikipedia.org/wiki/Binary_heap

在最坏的情况下,插入操作为O(logn),此后,最后插入的元素将成为子树的根,该子树具有以下属性:子树中的所有元素都小于根。

二进制堆通常用于实现优先级队列。

更直接的解决方案是使用间接索引对数组进行排序。索引将使您在当前元素右边的元素数量减少,因为这些元素使当前元素在具有精确计数位置的未排序数组中保持位置不正确。

#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

int arr[6] = {8, 1, 3, 10, 5, 6};
//int arr[6] = {1, 2, 3, 4, 5, 6};
vector<int> a(begin(arr), end(arr));
 // sort using a custom function object
struct {
    bool operator()(int idx1, int idx2) const
    {   
        return a[idx1] < a[idx2];
    }   
} custom_compare;

int main(int argc, char** argv) {
    vector<int> idx(a.size(), 0);
    vector<int> result(a.size(), 0);
    for (int i = 0; i < a.size(); i++) {
        idx[i] = i;
    }
    sort(idx.begin(), idx.end(), custom_compare);
    for (int i = a.size() - 1; i >= 0; i--) {
        result[idx[i]] = i - idx[i];
        result[idx[i]] = result[idx[i]] < 0 ? 0 : result[idx[i]];
    }

    for (int i = 0; i < a.size(); i++) {
        cout << result[i] << " ";
    }
    cout << endl;
    return 0;
}

对于简单的示例,idx如下所示:

1 2 4 5 0 3

因此,元素a [1] = 1应该在位置0,元素a [2] = 3应该在位置1,依此类推。如果我们看一下元素0,它位于已排序数组中的位置4和未排序数组中的位置0,那么有四个小于8的元素,将8、4个位置与其在排序数组中的位置分开。当然,对于位置不正确的数字,我们会得到负数,但是因为前面有更大的数字,所以我们将其设置为0。

运行该程序后,结果将如下所示:

4 0 0 2 0 0

所以8个比右边的小4个元素,而10个比他的小2个。